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

时间:2013-01-22 17:53:39

标签: c fork

我正在学习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 个答案:

答案 0 :(得分:6)

当您输出到文件时,stdio库会自动阻止缓冲出站位。

当程序调用{​​{1}}或从exit(2)返回时,任何剩余的缓冲位都会被刷新。

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

在这种情况下,main(),次调用会使结果更加复杂。这将复制每个子图像中部分填充和尚未刷新的I / O缓冲区。

在程序调用{​​{1}}之前,可以先使用fflush(3)刷新I / O.如果没有完成此刷新,那么您可能希望除了一个(通常是:子项)之外的所有进程都fork()而不是fork(),或从_exit(2)返回以防止输出相同的位不止一次。 (_exit(2)只是执行退出系统调用。)

答案 1 :(得分:4)

程序中的fork() if块内执行两次,因为一旦fork成功,程序将由两个进程控制( child 进程)。fork()块内的if由子进程和父进程执行。因此它将具有与预期不同的输出,因为它由两个不同的过程控制,并且它们的执行顺序未知。即。在每个fork()

之后,孩子或父母可以先执行

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

您写入缓冲区的内容(最终写入file(disk))无法保证写入文件(磁盘)immediatley。只有在执行 main() 完成后,才会将其刷新到磁盘。然而,在执行 main()期间,它会输出到终端

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

为了避免这种行为,使用fork()

在程序中进行三种不同的条件检查总是好的
int pid;
if((pid = fork()) == -1 )
{ //fork unsuccessful 
 }
else if ( pid > 0)
{ //This is parent
 }
else
{//This is child
 }

答案 2 :(得分:2)

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

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();
   }
}

另外,为了您的调试目的,最好转储进程&#39; ID,这样您就可以看到哪个进程产生了哪些进程,并且更好地了解了进展情况。 getpid()可以帮助您。

答案 3 :(得分:1)

  

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

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

所以你的问题可以通过多种方式解决:

  1. 通过fflush(3)
  2. 在fork之前使用显式缓冲区刷新
  3. 通过setvbuf(3)
  4. 手动设置缓冲区类型
  5. 使用write(2)代替stdlib的printf(3)
  6. 默认情况下,通过stderr *****
  7. 输出到fprintf(3)
  8. 在分叉流程中使用_exit(2)退出,而不是exit(3) ******
  9. 如果出现以下情况,最后两个可能无法正常工作:
    *默认情况下,您的实现不使用对stderr的无缓冲写入(ISO C要求) **你在child中编写了超过默认缓冲区大小的内容,并自动刷新了。

    <强> PS 即可。同样,如果您需要更深入的标准库函数和缓冲知识,我建议您阅读Advanced Programming in the UNIX Environment (2nd Edition) by W. Richard Stevens and Stephen A. Rago

    <强> PPS 即可。顺便说一句,你的问题是一个非常受欢迎的C / C ++程序员职位面试问题。