一个fork引发的血案

Reading time ~1 minute

在CoolShell上看到一道关于fork的题的讲解,但自己实验却发现事情并不是像他讲的那么简单,程序如下:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
 
int main(void)
{
   int i;
   for(i=0; i<2; i++){
      fork();
      printf("-");
   }
 
   return 0;
}

题目就是求解此程序会输出几个"-"。

要做这道题,首先要了解fork,fork会产生一个子进程,子进程和父进程都运行到fork返回处,只是子进程返回值为0,父进程的返回值则是新进程(子进程)的进程id,于是,据此可以算出这题应该是会输出6个"-"。

然而这样是不对的,本题的另一个关键点在于printf,printf输出到stdout,而stdout是有输出缓冲的,故此处i=1的时候做fork,子进程会将父进程的输出缓冲区也继承下来,这样事实上,每一个子进程都输出了2个"-",一共就是8个了。

但是,当我们将这个程序编译运行多次之后,会不幸的发现,结果并非和我们预料的一样,输出十分不稳定,2、4、6、8个的情况都有出现,难道是我们上面的分析错了?

我们先将程序修改如下:

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

int main(void)
{
    int i;
    int pid;
    pid = fork();
    printf("%d-", pid);
    pid = fork();
    printf("%d*", pid);
    return 0;
}

这样我们就可以看清楚每一个输出都是由哪个进程哪一次输出的了,以下是四次运行的结果:

➜  Sites  ./a
4687-4688*4687-0*0-4689*%
➜  Sites  ./a
4695-4696*4695-0*%
➜  Sites  ./a
4706-4707*%
➜  Sites  ./a
4716-4717*4716-0*0-4718*0-0*%

这四次结果正好涵盖了2、4、6、8的情况(%是zsh下没换行的情况下显示的标志)。

我们首先对4个进程编号:最初进程为1,1第一次fork出的子进程为2,1第二次fork出的子进程为3,2 fork出的子进程为4。

  • 查看8的情况:4716-4717*为1的输出,4716-0*为3的输出,0-4718*为2的输出,0-0*为4的输出;

  • 查看6的情况:4687-4688*为1的输出,4687-0*为3的输出,0-4689*为2的输出

  • 查看4的情况:4695-4696*为1的输出,4695-0*为3的输出

  • 查看2的情况:4706-4707*为1的输出

我们其实可以看出,任何一个进程如果输出了,那么他的父进程一定也输出了,而反之则不然。

排查下来看的话,这里应该就是由于父进程较早的退出,导致子进程的行为没有按照我们的预料,没能输出,至于为什么这里父进程退出后,子进程就没能输出,暂时我还没有研究清楚。

我们可以修改程序如下:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
 
int main(void)
{
   int i;
   for(i=0; i<2; i++){
      fork();
      printf("-");
   }
   wait(NULL);
   wait(NULL);
 
   return 0;
}

这样保证了父进程会等到子进程结束之后才会退出,于是乎再运行的时候,结果会稳定输出8个"-"。

注意这里一定要加2个wait,只加一个wait会出现如下情况:

➜  Sites  ./a
8390-0*8390-8391*0-0*%

1等待2结束,2输出了8390-0*,1输出了8390-8391*,3等待4结束,4输出了0-0*,但是1没有等待3结束,在3输出之前1就结束了,导致3没能输出。

挂载网络文件夹后网络故障时文件操作命令卡死

挂载 NFS 或者 Samba 的时候,经常会由于网络故障导致挂载好的链接断掉。此时如果尝试进行 ls、cd、df 等各种命令,只要与此目录沾上边,就会卡住。如果使用了类似 oh-my-zsh 这种配置的,只要在网络目录中,弹出命令提示符前就会直接卡住。这个时候第一反应就是...… Continue reading

路由折腾记 第四弹

Published on September 02, 2017