在C语言中使用printf()和fork()产生重复输出

8

我运行了这个小程序来测试fork(),但我无法弄清输出是什么,程序代码如下:

#include <stdio.h> 
#include <sys/types.h> 
#include <unistd.h> 

int i = 0;
void create()
{
    fork();
    printf("Inside i= %d \n", i);
    i = i + 1;
    fork();
}
int main()
{
    create();  
    return 0;
}

输出结果为:
Inside i= 0 
Inside i= 0 
Inside i= 0 
Inside i= 0 

难道不应该只有两个输出吗?因为在最后一个fork()中,子进程没有打印任何内容。

我读过这样一种说法,即在fork()之后,子进程将执行下一条指令,但是最后一个子进程似乎已经执行了printf()指令。


抱歉,我没有理解你的意思。 - OussamaMater
我稍微修改了问题的措辞,使其更加清晰易懂,希望您不介意。 - Marco Bonelli
printf() 不是线程安全的,因此您应该预料到会遇到麻烦。 - VillageTech
1
@VillageTech 线程安全与此无关。fork() 不会创建线程。 - Marco Bonelli
@Marco Bonelli - 你是对的,谢谢。 - VillageTech
父进程和子进程在调用fork()后立即继续执行。为了学习的目的,如果您想知道哪个进程(子进程或父进程)正在打印,请在printf()调用中添加当前pid。例如:printf("pid#%d: Inside i= %d \n", getpid(), i); - Rachid K.
1个回答

16

你的程序输出受到实现方式的影响。

C标准库对输出流应用缓冲,这意味着它会累积要写入的字符,直到缓冲区达到一定长度(或满足某些条件)然后一次性输出所有文本。

最常见的行为是使用行缓冲,这意味着遇到换行符(\n)时将打印文本。这确实是在我的机器上发生的事情。由于你在printf()之前进行了fork(),因此两个进程执行调用并且由于有一个换行符, 输出立即被打印:

$ ./prog
Inside i= 0
Inside i= 0

然后在这两个进程中的每个进程上执行另一个fork(), 但由于内部输出缓冲区已经被清空,所以没有更多可打印的内容,因此在这种情况下不会发生任何显著的事情。

然而,根据你的具体实现和程序运行的条件,printf()(以及一般的任何stdio函数)可能会决定应用不同的缓冲规则。

例如,当将输出导入到另一个程序或文件时,glibc通常使用固定大小的缓冲区并且不进行行缓冲。由于该缓冲区不是通过单个短的printf()填充的,文本会保留在其中以便稍后打印。当第二次fork()时,每个新子进程都会得到该缓冲区的副本,然后所有文本在进程退出时由它们中的每一个打印出来。在我的系统中,当导入输出时,这是输出结果:

$ ./prog | cat
Inside i= 0
Inside i= 0
Inside i= 0
Inside i= 0

如果您想确保文本立即打印,您可以使用 fflush() 或者禁用 stdout 的缓冲区,使用 setvbuf()

示例:

  • 使用 fflush()

void create()
{
    fork();
    printf("Inside i= %d \n", i);
    fflush(stdout);
    i = i + 1;
    fork();
}
  • 使用setvbuf()函数:

    int main()
    {
        setvbuf(stdout, NULL, _IONBF, 0);
        create();  
        return 0;
    }
    

  • @hyde 对的,不过现在我会进行一些关于缓冲区的研究并尝试修正想法,但如果有人能给出一个好的解释或一篇文章阅读,那我也不介意。 - OussamaMater
    @OussamaMater 这是一篇不错的文章:http://www.pixelbeat.org/programming/stdio_buffering/,还有这篇:https://eklitzke.org/stdout-buffering。 - Marco Bonelli
    第二个 fork() 的解释对我来说仍然不清楚,我会说这两个进程执行了写操作,这就是为什么输出了两次。此外,在第三段中有一个重复的 'the'。 - Mickael B.
    @MickaelB. 尝试让它更清晰。现在你明白了吗? - Marco Bonelli
    我有一个与编程无关的问题,我是stackoverflow的新手,我对@MarcoBonelli如何编辑我的问题感到困惑,现在肯定更好了,但是为什么会这样呢?我参观了一下,似乎没有提到这样的事情。 - OussamaMater
    @OussamaMater 用户可以编辑其他人的帖子(不仅限于他们自己的帖子)。如果您的声望低于2000,编辑将需要先获得批准,但如果您的声望更高,它将立即应用。人们经常会通过编辑来改善问题,修复措辞、错别字、格式等问题。 - Marco Bonelli

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接