fork 面试 集合

辅助调试方法:

getpid() returns the process ID of the current process. (This is often used by routines that generate unique temporary filenames.)

getppid() returns the process ID of the parent of the current process.

 

2012-3-11 21:17:34

还不懂作者最后画的两个图

 

 

关于fork的有意思的两道C语言题目

On September 29, 2010, in C语言, 笔试面试, 语言学习, by sponge

 

 

fork

是linux下的一个系统调用,它的作用是产生一个子进程,子进程是当前进程的一个副本,它跟父进程有一样的虚存内容,但也有一些不同点,读者可以自己参考这里

但是,值得注意的是,父进程调用fork()后,fork()返回的是生成的子进程(如果能顺利生成的话)的ID。子进程执行的起点也是代码中fork的位置,不同的是子进程fork()返回的是0。如下代码:

int i;
i
= fork();
printf("%d\n",i);

这段代码中,父进程打印出来的值是“子进程id”,而子进程打印出来的是0。

www.spongeliu.com

好了,废话介绍完了,让我们切入正题,看两个比较有意思的C语言题目。

www.spongeliu.com

第一题,计算下面代码理论上总共打印了多少行:(网易2011笔试题)

#include
#include
#include
int main(){
       
int i;
       
for(i = 0; i<5; i++){
                fork
();
               
printf("%d\n",getpid());
                fflush
(stdout);
       
}
}

//66:经过我的测试,我试图用全局变量或者 static变量累计计数。结果发现我忽视了 父子进程使用不同的 data

问题解答

这道问题并不难,最快的想法就是2+4+8+16+32,因为第一层的printf会有两个进程打印,第二层会增加到4个,以此往下,就得出62行。

www.spongeliu.com

 

但我这里打算采用另外一种方法,一种更加直观的方法,就是直接数出来,这样会避免大脑短路,而且对下一题目有帮助。

 

www.spongeliu.com

 

要直接数出来也很简单,只是有些繁琐,因为每循环一次,都会打印一行并且产生一个子进程,子进程又会继续循环打印并产生新的进程。我们可以在草稿纸上画一棵树,画出每个进程的子进程以及循环次数,如果你眼力够好,脑子不容易乱,这种方法很快会让你得到正确答案。但我恰好脑子不是能够保证清醒的人,画了三遍树得到的都是错误答案。

 

 

www.spongeliu.com

 

随后,我在纸上用了一种更简单的数据结构——队列进行计算,并且顺利得出了答案。我是这样计算的:

 

  1. 首先,主进程会循环5次,则我们将5压入到队列中:

queue =" 5 ";
sum
= 0; //sum是总打印次数

 

  1. 主进程会循环5次,打印5行并且产生5个子进程,这5个子进程分别会打印5,4,3,2,1行,则我们将这5个数放入队列,并将第一个5出队列加入到sum中:

queue = " 5 4 3 2 1 ";
sum
= sum + 5;

 

  1. 这样,我们再取队列首元素,即5,他会打印5行,并且生成4个子进程,子进程的分别会打印4,3,2,1行,我们把这4个数放入到队列中,并将第一个5出队列加入到sum中:

queue = " 4 3 2 1 4 3 2 1";

头部少,尾部加

 

sum = sum + 5;

  1. 我们继续重复上面的工作,取首元素4,他会打印4行,并且会声称3个子进程,子进程分别打印3,2,1行,重复上面的入队列和出队列操作:

queue = "  3 2 1 4 3 2 1 3 2 1 ";

头部少,尾部加上 递减序列

 

sum = sum + 4;

  1. 这样,以此重复以上的操作,当遇到元素1的时候,只有出队列而没有入队列的操作,因为只打印1行的子进程不会再循环产生新的子进程。最后,当队列中不再有元素的时候,sum就是总共打印的行数。

www.spongeliu.com

 

这种方法的有点是你可以很轻松、很清醒的在纸上把队列写出来并算出答案,缺点是如果你加法不好,很容易算错答案!

www.spongeliu.com

 

 

66

我也尝试了 跟踪,但是没成功额。作者用队列的方式。我想知道这不是共享变量实现的。 应为data隔离独立。。

 

作者的方法可以帮助分析

 

 

第二题:问下面的代码执行后总共产生了多少进程(不包括主进程)?(2009 EMC笔试)

 

 

#include
int main(){
        fork
();
        fork
() && fork() || fork();
        fork
();
}

 

 

这个题目跟上一个对比起来就稍微有点难度了,因为你就算画树也有可能算错!

www.spongeliu.com

 

我个人感觉这个题目考察两方面的知识:1、开头所讲的fork()返回值;2、&&和||的运算

www.spongeliu.com

 

让我们现讨论下&&和||的运算再来继续讨论这个题目。&&是“逻辑与”操作,如果两个操作数有一个为0,则整个式子为0。标准C中规定,如果&&运算符的左操作数为0,则计算右操作数;如果左操作数为1,才计算右操作数。

 

与之类似,||操作符是“逻辑或”操作,标准C规定如果||运算符左操作数为1,则不计算右操作数;如果左操作数为0,则计算右操作数。

www.spongeliu.com

 

继续来看我们的题目,我们把题目中的5个fork()分别标记为A,B,C,D,E。则我们可以看到,主进程一共产生4个进程,分别产生在A,B,C,E位置上(B,C两个fork()返回值都不是0,因此B&&C不为0,因此不计算D)。

 

让我们仍然采用上题的算法,使用一个队列:

 

 

  1. 首先,将主进程产生子进程的位置放到队列中:

queue = " A B C E ";
sum
= 0;

 

  1. 我们从队列中取首元素A,我们分析A处产生的进程,发现它会在B, C, E三处产生子进程,我们把这三个元素插入到队列中,并将sum+1。

queue = " B C E B C E "

66:加上头部以后的 剩余原队列

 

sum ++;

 

  1. 然后,我们从队列中取出首元素B,B处产生的子进程稍稍不一样,因为子进程中B所代表的fork()返回值为0,因此C得不到执行,而D会得到执行。因此,B处产生的子进程会执行D, E,将这两个元素送入队列,sum++:

queue = " C E B C E D E "

66难点

 

sum ++;

 

  1. 下面,我们取首元素C,分析发现,C处产生的进程会执行D, E,送入队列并且sum++:

queue = " E B C E D E D E "
sum
++;

 

  1. 同上一题一样,依次这样执行,遇到E则没有元素入队列,直到最后队列为空,sum就是总共产生的进程个数。

 

www.spongeliu.com

最后,总结下这两个题目,我感觉这里要想不搞乱,最好的方法就是这样子把并行的问题给串行话,因为就人脑来说,并行不一定比串行快^_^

欢迎大家留言进行讨论!

 

源文档 <http://www.spongeliu.com/%e8%af%ad%e8%a8%80%e5%ad%a6%e4%b9%a0/clanguage/%e5%85%b3%e4%ba%8efork%e7%9a%84%e6%9c%89%e6%84%8f%e6%80%9d%e7%9a%84%e4%b8%a4%e9%81%93c%e8%af%ad%e8%a8%80%e9%a2%98%e7%9b%ae/>

 

补充第二个问题:

答案是总共20个进程,出去main进程,还有19个进程。

我们再来仔细分析一下,为什么是还有19个进程。

p, li { white-space: pre-wrap; }

 

第一个fork和最后一个fork肯定是会执行的。

 

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

 

这里就需要注意&&和||运算符。

A&&B,如果A=0,就没有必要继续执行了;A非0,就需要继续执行&&B。

A||B,如果A非0,就没有必要继续执行了,A=0,就需要继续执行||B。

 

fork()对于父进程和子进程的返回值是不同的,按照上面的A&&B和A||B的分支进行画图,可以得出5个分支。

66

这个图中,关于2_2看的迷糊,还有3_2的分支

 

关键就是 子进程 执行的代码只管 父亲启动他后面那块!

还有 叶子节点都是 第三行!!!

 

Fork()      

 fork() && fork() || fork();
Fork();

 

clip_image001

加上前面的fork和最后的fork,所有的进程都会执行,会产生4个分支,总共4*5=20个分支,也就是20个进程,除去main主进程,就是19个进程了。

               

 

源文档 <http://bbs.chinaunix.net/thread-1947534-1-1.html>

原文地址:https://www.cnblogs.com/titer1/p/2390650.html