为什么 fflush() 会影响分叉进程的输出?
Why does fflush() affect the output of forked processes?
我正在尝试学习 UNIX 编程,遇到一个关于 fork()
的问题,我无法解释下面两个程序的输出。
我知道 fork()
创建了一个与当前 运行 进程相同的进程,但它从哪里开始?例如,如果我有下面这两个程序,输出结果是什么,它是如何工作的?
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main (int argc, char **argv)
{
int retval;
printf ("This is most definitely the parent process\n");
// now here fork will create a child process
// i need to know from which line child process starts execution
retval = fork ();
printf ("Which process printed this?\n");
return (0);
}
上面的程序和下面的程序有什么区别 关于子进程执行:
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
int main (int argc, char **argv)
{
int retval;
printf ("This is most definitely the parent process\n");
fflush (stdout);
// how does fflush change the output of above program and why ?
// even though no string operations are being used
retval = fork ();
printf ("Which process printed this?\n");
return (0);
}
我认为它们都应该打印:
This is most definitely the parent process
Which process printed this?
Which process printed this?
但是第一个正在打印:
This is most definitely the parent process
Which process printed this?
This is most definitely the parent process
Which process printed this?
执行将在 fork 调用时(或之后)继续。您可以使用 return 值来检查您是父进程还是子进程:
RETURN VALUE
On success, the PID of the child process is returned in the parent, and 0 is returned in the child. On failure, -1 is returned in the parent, no child process is created, and errno is set appropriately.
(来源:man fork
)
例如,如果您有以下程序:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char **argv) {
printf("Foo.\n");
int retval = fork();
printf("Bar from %s (%d).\n", (retval == 0) ? "child" : "parent", retval);
return 0;
}
输出类似于:
Foo.
Bar from parent (18464).
Bar from child (0).
...假设输出是行缓冲的。
I understand that fork() creates an identical process of the currently running process, but where does it start?
如果 fork(2)
成功(即不 return -1
),它从调用 fork(2)
的行开始。 fork(2)
return两次:它return在子进程中是0,在父进程中是正数C
,其中C
是进程ID新生儿。
您两次看到 这绝对是父进程 的原因与 stdio 的缓冲有关。用户 space 缓冲区中的 Stdio 缓冲区输出仅在某些情况发生时刷新(例如,缓冲区变满)。缓冲模式决定何时以及如何刷新缓冲区。
通常,如果输出被写入终端(或伪终端)等交互式设备,stdio 是 line-buffered,这意味着缓冲区在找到换行符或调用 fflush(3)
。
OTOH,如果输出重定向到文件或其他非交互式设备(例如,输出重定向到管道),stdio 是 fully-buffered,这意味着只有当缓冲区变满或调用 fflush(3)
时才会刷新缓冲区。
因此,如果没有 fflush(3)
,在终端设备中执行代码将打印:
This is most definitely the parent process
Which process printed this?
Which process printed this?
这是意料之中的。然而,如果你通过 cat(1)
管道它,你会看到这个(或其他一些变体,取决于执行顺序):
This is most definitely the parent process
Which process printed this?
This is most definitely the parent process
Which process printed this?
这是因为输出在重定向到管道时已完全缓冲。字符串 This is most definitely the parent process
不足以填充和刷新缓冲区,因此当父进程分叉时,子进程(获得父进程内存的副本 space)将获得输出缓冲区的副本,其中已经包含字符串 This is most definitely the parent process
。所以两个进程最终都打印了那个字符串。
如果你总是在 fork 之前调用 fflush(3)
,这不会发生,因为当父内存 space 被复制到子内存时缓冲区是空的。
当调用 fork()
时,有三种可能的 return 条件。
请阅读 fork()
三个return条件是
-1 -- the fork() failed
0 -- the child is executing
some positive number -- the pid of the child, the parent is executing
代码需要是这样的:
pid_t pid;
pid = fork();
if ( 0 > pid )
{ // then handle error
}
else if ( 0 == pid )
{ // then child executing
}
else // if ( 0 < pid )
{ // then parent executing
}