一:关于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执行时就有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");
}
结果如下图:
结果很美好,如我们所料,至此问题解决。