关于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()的调用处,整个父进程空间会原模原样地复制到子进程中,包括指令,变量值,程序调用栈,环境变量,缓冲区等等。
再去讨论就简单了,之所以会输出八个“+”,是因为printf(“+”)语句有buffer,所以对于上述程序,printf(“+”)把“+”放到了缓存中,在fork的时候,缓存也被复制到子进程空间,所以两个子进程就多输出2个“+”,一共是8个“+。

画个小图康康:(相同颜色表示同一个进程)
在这里插入图片描述
然后就能清楚知道,哪一个子进程复制了父进程标准输出缓存区里的内容,从而导致多次输出了“+”。
如下图,被标注了笑脸的就是多输出“+”的两个子进程。
在这里插入图片描述
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执行后就有25=10个进程;
当到最后一个fork执行时就有2
10=20个进程被创建;
所以除去main主进程,最终一共创建了19个进程。

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

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

结果如下图:
在这里插入图片描述
结果很美好,如我们所料,至此问题解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值