C linux中的fork()行为

C linux中的fork()行为,c,gcc,C,Gcc,我试图理解forks,并尝试使用C语言: #include<stdio.h> #include <unistd.h> void forker() { printf("%d: A\n",(int)getpid()); fork(); wait(); printf("%d: B\n",(int)getpid()); printf("%d: C\n",(int)getpid()); fork(); wait();

我试图理解forks,并尝试使用C语言:

#include<stdio.h>
#include <unistd.h>

void forker()
{
    printf("%d: A\n",(int)getpid());
    fork();
    wait();
    printf("%d: B\n",(int)getpid());
    printf("%d: C\n",(int)getpid());
    fork();
    wait();
    printf("%d: D\n",(int)getpid());
}

int main(void)
{
    forker();
    return 0;
}
但是,当我执行以下操作时:

> ./a.out > t.txt
奇怪的事情发生了:

> cat t.txt
3564: A
3565: B
3565: C
3566: D
3564: A
3565: B
3565: C
3565: D
3564: A
3564: B
3564: C
3567: D
3564: A
3564: B
3564: C
3564: D
有人能解释一下这种行为吗?为什么重定向到文件时输出不同


我使用的是Ubuntu 10.10,gcc版本4.4.5。

发生这种情况的原因是数据缓冲。在执行fork()时,在定向到文件的情况下,您的输出尚未刷新。。。因此,父级和子级现在都有未完成的输出缓冲区


调用
fflush(stdout)在每个
fork()之前
来解决这个问题。

问题是,
printf
的输出在发送到文件之前通过库缓冲区,这导致了您提到的奇怪行为。如果在每个
printf
之后添加
fflush(stdout)
,则文件中的输出也将正确


你可以在这里阅读更多信息:

其他答案并不能准确描述正在发生的事情,我必须多想一想才能理解。因此,在第二种情况下(由于文件重定向而缓冲输出),使用1、2、3和4代替3564、3565、3566和3567:

  • 进程1在其内部标准输出缓冲区中打印“A:1”
  • 进程1分叉并创建进程2,此创建意味着内部标准输出缓冲区的副本尚未打印
  • 进程1在其内部标准输出缓冲区中打印“B:1”和“C:1”,进程2打印“B:2”和“C:2”
  • 这两个进程都会分叉(在您的例子中是1->4和2->3,但可能不同),复制两个内部缓冲区
  • 所有4个进程在其缓冲区中打印D行,然后退出
此时,4个内部标准输出缓冲区的内容为:

- process 1:
    A:1
    B:1
    C:1
    D:1
- process 2:
    A:1
    B:2
    C:2
    D:2
- process 3:
    A:1
    B:2
    C:2
    D:3
- process 4:
    A:1
    B:1
    C:1
    D:4
  • 最后,以不确定的顺序打印4个缓冲区。在你的例子中,顺序是3,2,4,1

如果stdout是shell,或者使用
fflush()
,则不会发生此行为,因为stdout缓冲区在每个
fork()
之前转储,因此只复制空缓冲区。

是否确实显示了真实的代码
wait()
没有什么意义。@cnicutar:可能没有什么意义,但它不会导致可见的问题,因此这些调用确实可以在那里。我添加wait()只是为了确保父级等待子级完全执行。这是错误的/不必要的吗?好吧,因为一个
等待
需要一个参数。@soulcheck-在C中,使用一个没有原型的函数是合法的(错误的,但合法的)。我认为salil在这里很幸运,参数位置可能包含0,一个空指针,wait()必须检查并接受它而不使用。。。否则他会转储或覆盖内存位置。是的,谢谢。就这样。我读过关于fflush的文章,但是之前没有正确使用它。顺便说一句,为什么只有当我将它重定向到一个文件时,而不是当我在控制台上打印它时,才会出现问题?另外,为什么孩子从文件中的A开始打印???@Salil,请参见我在回答中提供的链接。它解释了当您重定向到文件时,如何在内核中缓冲标准输出。@Tudor,这只是缓冲问题吗?因为我正在打印的进程编号(PID)也被弄乱了。重定向时线程的执行顺序也会受到影响吗?@sail-执行顺序没有混乱,print()只执行一次。。。但是printf()并不直接将输出发送到目的地,它只是将输出发送到缓冲区。fork()复制缓冲区及其状态(导致缓冲区被发送到目的地两次),如果没有明确的线程管理,您无法控制哪个线程/进程将首先刷新其输出。感谢Tudor。该站点当前未启动。我过会儿再查。然而,当输出被传递到文件时,为什么子进程从“A”开始打印?此外,进程编号令人困惑。它们没有按顺序打印,甚至可能重叠,因为文件写入偏移量对于所有进程都是相同的,并且不会随写入而自动更新。当输出是一个伪tty设备(不是shell!)时,不会发生这种情况,因为这样的设备无法进行查找,并且文件写入偏移量更新的原子性问题无关紧要。为了避免与常规文件重叠,它们应该设置O_APPEND标志。根据POSIX,对文件的并发写入行为是未指定的。
- process 1:
    A:1
    B:1
    C:1
    D:1
- process 2:
    A:1
    B:2
    C:2
    D:2
- process 3:
    A:1
    B:2
    C:2
    D:3
- process 4:
    A:1
    B:1
    C:1
    D:4