2016-10-05 36 views
2

我正在运行以下代码,这是我在Mac上用c写的。拨打电话后打印出来的额外数据()

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

int main(int argc, const char * argv[]) { 

    int i = 0; 
    printf("Starting pid:%d\n", getpid()); 
    while(i++<3){ 
     int ret = fork(); 
     printf("hello(%d)%d, %d ", i, getpid(), ret); 
     if(!ret){ 
      printf("\n"); 
      return 0;} 
    } 
} 

我脑子里想的目的是为了有6幅版画总 - 从原来的父3(PID“启动PID”打印),并从每个3个催生孩子的1以上。相反,我收到的是:

Starting pid:7998 
hello(1)8009, 0 
hello(1)7998, 8009 hello(2)8010, 0 
hello(1)7998, 8009 hello(2)7998, 8010 hello(3)7998, 8011 hello(1)7998, 8009 hello(2)7998, 8010 hello(3)8011, 0 

而且,原父(7998)打印6次(三次当我== 1次,用我== 2,和曾经与我== 3)。

为什么会发生这种情况?为什么我没有收到来自父母的三张照片(一次当我== 1,我== 2,我== 3)。如果我们从父母的角度来看它 - 有一个while循环,我们应该只打印3次。

叉可能是异步或什么的?我错过了什么?

回答

7

当你在fork一个进程中,父进程中的内存被复制到子进程中。这包括stdout缓冲区。

从父级循环的第二次迭代开始,缓冲区中有hello(1)7998, 8009。当第二个孩子分叉时,这个文本仍然在那个孩子的缓冲区中。第二个孩子然后打印hello(2)8010, 0,然后换行。换行刷新孩子的缓冲器,所以第二子打印这样的:

hello(1)7998, 8009 hello(2)8010, 0 

叉之后,其被添加到输出缓冲器中的父打印hello(2)7998, 8010。然后在第三次迭代,第三个孩子继承了这一点,并打印:

hello(1)7998, 8009 hello(2)7998, 8010 hello(3)8011, 0 

父然后打印hello(3)7998, 8011这再次加入缓冲液。然后父母然后退出循环和退出,在此之后,缓冲刷新和父输出:

hello(1)7998, 8009 hello(2)7998, 8010 hello(3)7998, 8011 

为了纠正这一点,那么父进程需要分叉之前清除输出缓冲区。

这可以通过调用printf后主叫fflush(stdout),或通过添加一个新行(\n)到printf的端部来实现。

编辑:

作为一个更通用的解决方案,呼吁fflush(NULL)将刷新所有打开的输出流。

+0

优秀。重要的是缓冲区内容的复制。 –

+0

哇..只是哇。 –

+1

'fflush(NULL)'刷新所有打开的输出文件。 –

0

叉将过程分为两部分。在每个子进程中。你最终在3分叉之后有8个进程(除非你有条件地做)

+2

我立即杀死子进程 - 他们不分叉。 –

+0

嗯,这是一个有趣的事情,现在你提到... –

+0

而且我看到其中一个孩子产生了一个子女。 –

3

我认为你所看到的只是流程本地内容 - 我将最终 - 写入到 - 缓冲区正被复制到fork上,因此在fork之前排队的内容将被多个进程单独输出。

分叉是做你的想法,措施的工具是有缺陷的。

我修改你的代码中添加冲洗:

int ret = fork(); 
    printf("hello(%d)%d, %d ", i, getpid(), ret); 
    fflush(stdout); 

这保证了printf的结果,在过程中本地缓冲区不坐。它直接写入stdout,然后本地缓冲区被清空。然后输出变为:

Starting pid:41731 
hello(1)41731, 41732 hello(2)41731, 41733 hello(1)41732, 0 
hello(3)41731, 41734 hello(2)41733, 0 
hello(3)41734, 0 

即预期的6次呼叫。 (我在Mac上使用CodeRunner来测试它;它也显示了9个结果,没有flush,所以我相信我重复了初始条件)。

+0

完美!谢谢!什么是冲洗干什么? –

+0

@JohnBollinger在同一时间,使用flush可以解决这个问题,这很奇怪 –

+0

只要确保该进程正在写入所有必须标准输出的东西,而不是将其保存在内部缓存中。我会下注缓存,当你fork时,所有hello(1)7998实际上并不是来自进程7998,它们只是被进程7998入列,然后被排入队列的文本被复制过来。由于'fflush'清除缓冲区,因此没有任何可复制的内容。 (编辑:和答案修改,因为这是一个相当令人信服的原因) – Tommy