天天看点

关于fork的面试经典题

一:关于fork的一些基本知识

1.一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。

2. 一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。

3. fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:

1)在父进程中,fork返回新创建子进程的进程ID;

2)在子进程中,fork返回0;

3)如果出现错误,fork返回一个负值

来一个简单例子

#include<stdio.h>
int main(int argc, char *argv[])
{
    if (fork() == 0) {
	printf("Hello from child\n");
    }
    else {
	printf("Hello from parent\n");
    }
    return 0;
}
           

运行结果是:

Hello from parent

Hello from child

(假设大家都已经掌握fork基本知识,接下来看看有趣的几个题)

二:面试题解析

1.请问下面的程序一共输出多少个“+”?

#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的机制的你,可能马上会想到会输出6个“+”,然而跑一下惊奇地发现输出了8个“+”(有图有真相)

关于fork的面试经典题

那这又是为何呢,首先我们要知道,在fork()的调用处,整个父进程空间会原模原样地复制到子进程中,包括指令,变量值,程序调用栈,环境变量,缓冲区等等。

再去讨论就简单了,之所以会输出八个“+”,是因为printf(“+”)语句有buffer,所以对于上述程序,printf(“+”)把“+”放到了缓存中,在fork的时候,缓存也被复制到子进程空间,所以两个子进程就多输出2个“+”,一共是8个“+。

画个小图康康:(相同颜色表示同一个进程)

关于fork的面试经典题

然后就能清楚知道,哪一个子进程复制了父进程标准输出缓存区里的内容,从而导致多次输出了“+”。

如下图,被标注了笑脸的就是多输出“+”的两个子进程。

关于fork的面试经典题

2.请问下面的程序创建了多少进程(不包括main进程)?

#include <stdio.h>  
int main(int argc, char* argv[])  
{  
   fork();  
   fork() && fork() || fork();  
   fork();  
}  
           

本题的关键在于理解fork()&& fork()|| fork()语句会怎样执行;为了遵守循序渐进的原则,我们不妨先看一个简单一点的一个程序:

int main()
{
	fork()&&fork();
	printf("+");
	return 0;
}
           

输出为三个“+”;

还记得逻辑与“A&&B”的语法规则吗,只有A,B两个操作数同时为真,结果才为真,如果A为假,则右边被短路,即B无论为什么都不再执行;

那么此程序在main这个主进程中,首先执行 fork() && fork(), 左边的fork()返回一个非零值,根据&&的执行原则,前面的表达式为真时,后面的表达式继续执行,故包含main的这个主进程创建了一个子进程,然后主进程继续执行右边fork()&& fork(),右边fork创建一个子进程。

由于子进程会复制父进程,而且子进程会根据其返回值继续执行,就是说,在子进程中, fork() &&fork()这条语句左边表达式的返回值是0, 所以&&右边的表达式不会执行,这时在子进程不再创建新的进程,打印“+”后退出。

即main进程创建两个子进程,整个一共创建了3个进程。

再看有关逻辑或的一个简单程序:
int main()
{
	fork()||fork();
	printf("+");
	return 0;
}
           

这个程序也是要先搞清楚“A||B”的执行规则,当A为真的时候,B被短路,A为假的时候才执行B。

在main这个主进程中,首先执行 fork() || fork(), 左边的fork()返回一个非零值,根据||的短路原则,前面的表达式为真时,后面的表达式不执行,故包含main的这个主进程创建了一个子进程,

由于子进程会复制父进程,而且子进程会根据其返回值继续执行,就是说,在子进程中, fork() ||fork()这条语句左边表达式的返回值是0, 所以||右边的表达式fork()要执行,这时在子进程中又创建了一个进程,

即main进程->子进程->子进程,一共创建了3个进程。

再回到之前的题,我们看到第一个fork和最后一个fork肯定是会执行的。

主要在中间3个fork上,可以画一个图进行描述:

关于fork的面试经典题

可以看出来一共有五个分支

当第一个fork执行后就有25=10个进程;

当到最后一个fork执行时就有210=20个进程被创建;

所以除去main主进程,最终一共创建了19个进程。

最后我们检验一下:

加一句语句printf(“ok\n”)看是不是如我们推理的19个进程

#include <stdio.h>  
int main(int argc, char* argv[])  
{  
   fork();  
   fork() && fork() || fork();  
   fork();  
   printf("ok\n");
}  
           

结果如下图:

关于fork的面试经典题

结果很美好,如我们所料,至此问题解决。

继续阅读