4

我正在学习与 合作fork(),我有一些问题。

考虑以下代码:

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

int main()
{
   int i;

   for(i = 0; i < 5; i++)
   {
      printf("%d", i);

      if((i%2)==0)
         if(fork())
            fork();
   }
}

当我输出到终端时,我得到了我期望的结果(即:)0,1,1,1,2,2,2,...。但是当我输出到文件时,结果完全不同:

  • 案例1:(输出到终端,例如:)./a.out

    结果是:0,1,1,1,2,2,2,...

  • 案例2:(输出到文件,例如./a.out > output_file:)

    结果是:0,1,2,3,4,0,1,2,3,4,0,1,2,3,4,...

为什么会这样?

4

4 回答 4

6

当您输出到文件时,stdio 库会自动对出站位进行块缓冲。

当程序调用exit(2)或从main(),任何剩余的缓冲位返回时被刷新。

在这样一个不会产生太多输出的程序中,所有的 I/O 都将main(),在目标不是 tty 时返回之后发生。这通常会自行改变 I/O 操作的模式和顺序。

在这种情况下,结果会因一系列fork()调用而变得更加复杂。这将在每个子图像中复制部分填充和尚未刷新的 I/O 缓冲区。

在程序调用之前,fork(),可能首先使用 fflush(3) 刷新 I/O。如果未完成此刷新,则您可能希望除一个之外的所有进程(通常:子进程)来_exit(2)代替exit(3)或返回,main(),以防止多次输出相同的位。(_exit(2) 只是执行退出系统调用。)

于 2013-01-22T17:57:06.053 回答
4

程序中的fork()内部if块执行两次,因为一旦fork成功,程序由两个进程(进程和进程)控制。所以fork()内部if块由子进程和父进程执行。因此它的输出将与预期不同,因为它由两个不同的进程控制,并且它们的执行顺序未知。IE。孩子或父母可以在每个之后先执行fork()

对于输出和文件之间的行为差​​异。这就是原因。

您写入缓冲区(file(disk)最终写入)的内容不能保证立即写入文件(磁盘)。只有在main()执行完成后,它才主要刷新到磁盘。而在main()执行期间,它会输出到终端

在写入磁盘中的文件之前,kernel实际上将数据复制到缓冲区中,然后在后台,内核收集所有脏缓冲区,对它们进行最佳排序并将它们写入文件(磁盘)。这称为writeback。它还允许内核将写入推迟到更多的空闲时段,并将许多写入批处理在一起

为了避免这种行为,在程序中使用三种不同的条件检查总是好的fork()

int pid;
if((pid = fork()) == -1 )
{ //fork unsuccessful 
 }
else if ( pid > 0)
{ //This is parent
 }
else
{//This is child
 }
于 2013-01-22T17:58:54.057 回答
2

缓冲流有时会产生一些奇怪的结果......尤其是当您有多个进程使用相同的缓冲流时。强制刷新缓冲区,你会看到不同的结果:

int main()
{
   int i;
   FILE * fd = fopen(yourfile, "w");
   for(i = 0; i < 5; i++)
   {
      fprintf(fd, "%d", i);
      fflush(fd);
      if((i%2)==0)
         if(fork())
            fork();
   }
}

此外,出于调试目的,最好转储进程的 ID,以便您可以查看哪个进程产生了哪个进程,并更好地了解正在发生的事情。getpid()可以帮助你。

于 2013-01-22T18:07:03.223 回答
1

为什么分叉时终端和文件之间的输出不同?

C 标准库函数使用内部缓冲来加速。大多数实现对文件流使用完全缓冲的 IO,对 stdin/stdout 使用行缓冲,对 stderr 使用无缓冲的 IO。

因此,您的问题可以通过多种方式解决:

  1. 在 fork via 之前使用显式缓冲区刷新fflush(3)
  2. 通过手动设置缓冲区类型setvbuf(3)
  3. 使用write(2)而不是 stdlib 的printf(3)
  4. stderr默认通过fprintf(3)*****输出到
  5. 在分叉的进程中退出_exit(2)而不是exit(3)******

如果出现以下情况,最后两个可能无法按预期工作:
* 默认情况下,您的实现不使用对 stderr 的无缓冲写入(这是 ISO C 要求的)
** 您在子项中写入的缓冲区大小超过了默认缓冲区大小,并且如果被自动刷新。

附言。再一次,如果您需要更深入地了解标准库函数和缓冲,我建议您阅读W. Richard Stevens 和 Stephen A. Rago 的《UNIX 环境中的高级编程》(第 2 版)

聚苯乙烯。顺便说一句,您的问题是 C/C++ 程序员职位的一个非常受欢迎的面试问题。

于 2013-01-23T06:02:12.433 回答