操作系统:Linux,语言:纯C
我正在学习一般的 C 编程,并在特殊情况下学习 UNIX 下的 C 编程。
在使用 fork()
调用后,我检测到 printf()
函数有一个奇怪的(对我来说)行为。
代码
#include <stdio.h>
#include <system.h>
int main()
{
int pid;
printf( "Hello, my pid is %d", getpid() );
pid = fork();
if( pid == 0 )
{
printf( "\nI was forked! :D" );
sleep( 3 );
}
else
{
waitpid( pid, NULL, 0 );
printf( "\n%d was forked!", pid );
}
return 0;
}
输出
Hello, my pid is 1111
I was forked! :DHello, my pid is 1111
2222 was forked!
为什么第二个“Hello”字符串出现在 child 的输出中?
是的,这正是父级启动时打印的内容,带有父级的pid
。
但是!如果我们在每个字符串的末尾放置一个 \n
字符,我们将得到预期的输出:
#include <stdio.h>
#include <system.h>
int main()
{
int pid;
printf( "Hello, my pid is %d\n", getpid() ); // SIC!!
pid = fork();
if( pid == 0 )
{
printf( "I was forked! :D" ); // removed the '\n', no matter
sleep( 3 );
}
else
{
waitpid( pid, NULL, 0 );
printf( "\n%d was forked!", pid );
}
return 0;
}
输出:
Hello, my pid is 1111
I was forked! :D
2222 was forked!
为什么会这样?这是正确的行为,还是错误?
最佳答案
我注意到 <system.h>
是非标准 header ;我将其替换为 <unistd.h>
并且代码编译得很干净。
当您的程序输出到终端(屏幕)时,它是行缓冲的。当程序的输出进入管道时,它是完全缓冲的。您可以通过标准 C 函数控制缓冲模式 setvbuf()
和 _IOFBF
(全缓冲),_IOLBF
(行缓冲)和 _IONBF
(无缓冲)模式。
您可以在修改后的程序中证明这一点,方法是将程序的输出通过管道传输到,比如说,cat
。 .即使在 printf()
末尾有换行符字符串,你会看到双重信息。如果您直接将其发送到终端,那么您只会看到大量信息。
这个故事的寓意是要小心调用fflush(0);
在 fork 之前清空所有 I/O 缓冲区。
按要求逐行分析(删除大括号等 - 并由标记编辑器删除前导空格):
-
printf( "Hello, my pid is %d", getpid() );
-
pid = fork();
-
if( pid == 0 )
-
printf( "\nI was forked! :D" );
-
sleep( 3 );
-
else
-
waitpid( pid, NULL, 0 );
-
printf( "\n%d was forked!", pid );
分析:
- 将“你好,我的 pid 是 1234”复制到标准输出的缓冲区中。因为末尾没有换行符并且输出以行缓冲模式(或全缓冲模式)运行,所以终端上没有任何显示。
- 为我们提供两个独立的进程,在标准输出缓冲区中使用完全相同的 Material 。
- child 有
pid == 0
并执行第 4 行和第 5 行;父级的pid
具有非零值(这两个进程之间的少数差异之一 - 来自getpid()
和getppid()
的返回值还有两个)。 - 添加一个换行符和“I was forked! :D”到 child 的输出缓冲区。第一行输出出现在终端上;其余部分保存在缓冲区中,因为输出是行缓冲的。
- 一切都停止了 3 秒。这之后子进程通过main末尾的return正常退出。那时,标准输出缓冲区中的残留数据被刷新。由于没有换行符,因此将输出位置留在行尾。
- 家长来了。
- parent 等待 child 结束死亡。
- parent 添加了一个换行符和“1345 被 fork 了!”到输出缓冲区。换行符将“Hello”消息刷新到输出,在 child 生成的不完整行之后。
parent现在通过main末尾的return正常退出,残留数据被flush;由于最后还是没有换行符,光标位置在感叹号之后,shell提示出现在同一行。
我看到的是:
Osiris-2 JL: ./xx
Hello, my pid is 37290
I was forked! :DHello, my pid is 37290
37291 was forked!Osiris-2 JL:
Osiris-2 JL:
PID 编号不同——但整体外观一目了然。在 printf()
的末尾添加换行符语句(很快成为标准做法)会大大改变输出:
#include <stdio.h>
#include <unistd.h>
int main()
{
int pid;
printf( "Hello, my pid is %d\n", getpid() );
pid = fork();
if( pid == 0 )
printf( "I was forked! :D %d\n", getpid() );
else
{
waitpid( pid, NULL, 0 );
printf( "%d was forked!\n", pid );
}
return 0;
}
我现在得到:
Osiris-2 JL: ./xx
Hello, my pid is 37589
I was forked! :D 37590
37590 was forked!
Osiris-2 JL: ./xx | cat
Hello, my pid is 37594
I was forked! :D 37596
Hello, my pid is 37594
37596 was forked!
Osiris-2 JL:
请注意,当输出到达终端时,它是行缓冲的,所以 'Hello' 行出现在 fork()
之前而且只有一份。当输出通过管道传输到 cat
时, 它是完全缓冲的,所以在 fork()
之前没有任何内容出现并且两个进程在缓冲区中都有要刷新的“Hello”行。
关于c - "fork()"后printf异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49813401/