我有以下代码:
#include <stdio.h>
int main() {
printf("Hello\n");
fork();
return 0;
}
这给出了输出:
Hello
符合预期。但是,如果我将代码修改为:
#include <stdio.h>
int main() {
printf("Hello");
fork();
return 0;
}
删除,\ n提供输出:
HelloHello
为什么printf被调用两次。子进程是否不应该执行下一条指令:return 0;
答案 0 :(得分:3)
printf
函数调用将hello
字符放入与stdout
流相关联的缓冲区中。随后,当进程退出时(即我们看到输出时),将刷新缓冲区。您已经在此发生之前分叉了,因此当两个进程各自退出时,两个进程将在两个单独的地址空间中执行此缓冲区刷新。每个进程都有一个流的副本,其中包含缓冲区及其hello
内容。
当stdout
流连接到交互式设备(如Unix上的TTY)时,将对其进行行缓冲。行缓冲意味着每当输出换行符时都会刷新缓冲区。
如果我们在fork
之前刷新缓冲区(例如通过打印换行符或通过调用fflush(stdout)
),则刷新将在父进程中进行。 fork
时缓冲区为空;尽管子进程继承了它的副本,但是在任何一个进程中都没有刷新的对象。
在输出重复的情况下,实际上 被调用了两次。它不是printf
,而是write
系统调用,它将缓冲的字符发送到输出设备。
答案 1 :(得分:1)
它没有被两次调用。请记住,默认情况下,与控制台连接的stdout
是行缓冲的。由于您还没有用换行符结束printf
参数,因此它不会进入控制台。它只会先进入缓冲区。因此,printf("Hello");
将"Hello"
复制到输出缓冲区中,fork()
使用地址空间的副本(包括stdout
的输出缓冲区和{{ 1}}中的字符串)和"Hello"
将控制权返回给return 0;
,这将刷新输出缓冲区,但是由于这是在libc
之后发生的,因此它将发生两次-一次父级,一次进入子级,因此最终输出中将得到fork()
。