CSAPP Chap8 关于Fork函数的一些练习与分析

来源:互联网 发布:java volatile用法 编辑:程序博客网 时间:2024/06/10 03:58

今天做了第8章的作业,感觉自己对fork函数的一些地方理解的不够深入,又怕期末考到这方面的题,就想在这方面理解的深入点,这篇文章只针对书上的练习给出一些比较合理的解释。
首先做这部分的题目需要上机验证,这就涉及到代码怎么运行的问题,都知道要加头文件csapp.h和csapp.c
但是在windos下运行这些代码都是有问题的,我查了一些资料发现要在linux下用gcc编译,需要将这两个文件放入user/include中,这个地址是只读不可写的,所以要用ubuntu下的root下的复制文件指令sudo 文件名 地址,并在gcc编译时加-lpthread。
前言就说到这里,接下来是关于fork函数的练习与分析。
8.13
求解可能输出,代码如下

#include "csapp.h"int main(){    int x=3;    if(Fork()!=0)    {        printf("x=%d\n",++x);    }    printf("x=%d\n",--x);        exit(0);}

分析一下结果还是比较容易看的,这类题最好画图做,但是我有点懒就不把图传上来了。
fork函数生成一个子进程,对于父进程要执行printf(“x=%d\n”,++x);语句,然后对于子进程和父进程,都要执行printf(“x=%d\n”,–x);语句,所以这个问题的结果是
x=4 父
x=3 父
x=2 子
或者
x=4 父
x=3 子
x=2 父
往这上面传图好麻烦,比较简单的题就不传了,ubuntu下验证和这个结果一样。
后面我想了一下,上面这个解释是不对的
fork函数的性质是“父进程和子进程对x所做的任何改变都是独立的,不会反映在另一个进程的存储器中”,意思就是【x父】和【x子】是两个不同的变参,考虑下面的程序

#include "csapp.h"int main(){    int x=3;    if(Fork()!=0)    {        printf("x=%d\n",++x);    }    else printf("x=%d\n",--x);    //printf("x=%d\n",--x);        exit(0);}

这个程序对于x父和x子做不同的修改,打印值应该为
x=4 父
x=2 子
所以对于第一个例子,有了更合理的解释:对于父进程中的x,先做++x,输出x=4,然后再做–x,输出x=3,而对于子进程中的x,输出的一定是–x,即x=2

类似的考虑方法可以考虑以下代码:

#include "csapp.h"int main(){    int x=3;    if(Fork()!=0)    {        printf("x=%d\n",++x);    }    else printf("x=%d\n",--x);    printf("x=%d\n",--x);        exit(0);}

这次就很容易分析了,结果是4 3 2 1,此处不赘述了 。

8.14
求hello会输出多少行

#include "csapp.h"void doit(){    if(Fork()==0)    {        Fork();        printf("hello\n");        exit(0);    }}int main(){    doit();    printf("hello\n");    exit(0);}

我觉得在doit函数中,子进程生成两个进程,并都打印hello,然后回到主函数中,这些所有的进程都打印,加起来应该是2+3=5条。
但是验证后只有3条,想一想原因是在doit中的if语句内,执行完毕后exit(0),子进程和他创建出的进程都永远截止了,那么在执行完doit函数回到主函数中的时候,就只剩下一个父进程了,所以答案是2+1=3条
为了验证可以使用如下代码

#include "csapp.h"void doit(){    if(Fork()==0)    {        Fork();        printf("hello\n");        //exit(0);    }}int main(){    doit();    printf("hello\n");    exit(0);}

相比原来的就是在exit(0)上加了注释,所以这次打印5行。上面这个例子其实就是8.15题。

8.16

#include "csapp.h"int counter=1;int main(){        if(Fork()==0)        {            counter--;            exit(0);        }        else        {            Wait(NULL);            printf("counter=%d\n",++counter);        }            exit(0);}

这题比较简单,针对子进程,修改了他的全局变量counter子,然后将该进程结束,对于父进程,做++counter并输出,故结果应该为counter=2
这里要注意的是,全局变量也是父进程和子进程各自独有的,而非共有。

8.18

#include "csapp.h"void end(void){    printf("2");}int main(){    if(Fork==0)    atexit(end);    if(Fork==0)    printf("0");    else    printf("1");    exit(0);}

题目中说了这个atexit函数是一一个指向函数的指针为输入,并将其添加到初始为空的函数列表中,当exit函数调用时会调用这个列表中的函数

我感觉这个题是最难的,因为这题要考虑到第一次运行fork时,对于第一个子进程调用atexit函数,在下一次的fork函数中,这个子进程生成的两个子进程的堆栈中都存了所谓的函数列表,并在最后的exit调用时会调用它们,所以会输出两个2,而这题的结果只要满足1-2,0-2的结构就可以了。

2016.6.8

0 0
原创粉丝点击