几个fork面试题目的验证

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

 

  1. int   
  2. main(){  
  3.     int i;  
  4.     for(i = 0; i<5; i++){  
  5.         fork();  
  6.         printf("%d\n",getpid());  
  7.         fflush(stdout);  
  8.     }  
  9. }  


问题解答:

问题的答案为62,基本方法为2+4+8+16+32=62,可以认为是每次进程数目加倍,输出的数目实际对应进程的数目,另外原文提出了另一种解决此问题的方法,可以参考原文。

但是如果用程序验证,就不可能得到正确的答案,所以在题目中说明是“理论上“。因为在主进程结束之前,子进程并不一定能够完成执行,为了保证子进程的顺利结束,应当在每一个fork()之后增加wait(),等待子进程结束。验证程序代码如下,

 

  1. int   
  2. main(){  
  3.     int i;  
  4.     int status;  
  5.     pid_t pid;  
  6.     for(i = 0; i<5; i++){  
  7.         fork();  
  8.         pid=wait(&status);  
  9.         printf("pid=%d, ppid=%d, child=%d\n",getpid(), getppid(), pid);  
  10.         fflush(stdout);  
  11.     }  
  12. }  

可以发现最后输出预期的62行,而且可以通过统计最后的child为-1的值,得到产生的子进程的总数目。因为第一次产生子进程时候,子进程运行到此会因为没有子进程而返回-1错误,可以用perror()查看错误原因为”No child processes"。

 

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

 

  1. #include  
  2. int main(){  
  3.         fork();  
  4.         fork() && fork() || fork();  
  5.         fork();  
  6. }  

题目的解法在原文中可以得到,这里列出基本解决方法,验证才是本文重点。

题目考察两方面的知识:1、fork()返回值;2、&&和||的运算。

&&是“逻辑与”操作,如果两个操作数有一个为0,则整个式子为0。标准C中规定,如果&&运算符的左操作数为0,则不计算右操作数;如果左操作数为1,才计算右操作数。与之类似,||操作符是“逻辑或”操作,标准C规定如果||运算符左操作数为1,则不计算右操作数;如果左操作数为0,则计算右操作数。

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

因为同样的原因看,用程序无法直接得到进程数目。而且此处不像上一个题目一样能够在fork()之后增加wait()操作。我们仍然采用增加wait()操作的方法,方法为对fork()函数进行包裹,函数的形参与返回值与原函数相同,不过在中间增加wait()。如此可以保证子进程顺利结束。而且在程序的最后增加一个输出操作,输出的操作符数目即是进程总数目,但是记住输出一定要使用换行符"\n"或者清空缓冲区,否则最后的输出符号数目并不是进程的总数目,此处将在第三题说明。代码如下:

 

  1. int main(int argc, char** argv)  
  2. {  
  3.    int i;  
  4.    int status;  
  5.    pid_t pid;  
  6.   
  7.    fflush(stdout);  
  8.   
  9.    Fork();  
  10.    Fork() && Fork() || Fork();  
  11.    Fork();  
  12.    printf("+\n");  
  13.   
  14.    return 0;  
  15. }  
  16.   
  17. pid_t Fork()  
  18. {  
  19.    int status;  
  20.    pid_t pid;  
  21.    pid = fork();  
  22.   
  23.    wait(&status);  
  24.    return pid;  
  25. }  

最终的输出结果为20,除去主进程,产生了19个子进程。

 

第三题:请问下面的程序输出多少个“-”符号。原文讲的较好,本文就不在重新解释,只是认为这是三个题目联系较大,可以一起学习。

题目来源:

  1. #include <stdio.h>  
  2. #include <sys/types.h>  
  3. #include <unistd.h>  
  4.    
  5. intmain(void)  
  6. {  
  7.    inti;  
  8.    for(i=0; i<2; i++){  
  9.       fork();  
  10.       printf("-");  
  11.    }  
  12.    
  13.    return0;  
  14. }  

分析fork()的机制比较熟悉的话,这个题并不难,输出应该是6个“-”,但是,实际上这个程序会很tricky地输出8个“-”。

要讲清这个题,我们首先需要知道fork()系统调用的特性

(1) fork()系统调用是Unix下以自身进程创建子进程的系统调用,一次调用,两次返回,如果返回是0,则是子进程,如果返回值>0,则是父进程(返回值是子进程的pid),这是众为周知的。

(2) 还有一个很重要的东西是,在fork()的调用处,整个父进程空间会原模原样地复制到子进程中,包括指令,变量值,程序调用栈,环境变量,缓冲区,等等。

所以,上面的那个程序为什么会输入8个“-”,这是因为printf(“-”);语句有buffer,所以,对于上述程序,printf(“-”);把“-”放到了缓存中,并没有真正的输出,在fork的时候,缓存被复制到了子进程空间,所以,就多了两个,就成了8个,而不是6个。

另外,多说一下,我们知道,Unix下的设备有“块设备”和“字符设备”的概念,所谓块设备,就是以一块一块的数据存取的设备,字符设备是一次存取一个字符的设备。磁盘、内存都是块设备,字符设备如键盘和串口。块设备一般都有缓存,而字符设备一般都没有缓存。

对于上面的问题,我们如果修改一下上面的printf的那条语句为:

 

  1. printf("-");  
  2. fflush(stdout);  
或者
  1. printf("-\n");  

就没有问题了(就是6个“-”了),因为程序遇到“\n”,或是EOF,或是缓中区满,或是文件描述符关闭,或是主动flush,或是程序退出,就会把数据刷出缓冲区。需要注意的是,标准输出是行缓冲,所以遇到“\n”的时候会刷出缓冲区,但对于磁盘这个块设备来说,“\n”并不会引起缓冲区刷出的动作,那是全缓冲,你可以使用setvbuf来设置缓冲区大小,或是用fflush刷缓存。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值