linux fork() 进程树的两种实现方法

Linux fork()进程树的两种实现方法


刚完成操作系统的实验一,其中附加题是使用fork()实现一颗满二叉树形态的进程树,觉得好玩,做完之后就记录一下。


1. 暴力的做法

最简单粗暴的做法,当然就是直接把层数写死啦。例如要创建三层的进程树,就父进程fork()两次,然后在子进程里又fork()两次,这样就有三层了,且最后fork()出来的那层就不再继续生成,直接打印出“我的PID是xxx,我爸是xxx”就可以跟它say goodbye了。

下面贴一下拙略的代码:

#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>

int main(){
    pid_t pid[2];// 记录生成的两个子进程的PID

    printf("Root pid is %d.\n",getpid());

    for( int i=0;i<2;++i ){
        pid[i] = fork();

        if( pid[i]==0 ){// 子进程
            printf("My parent is %d, my pid is %d.\n",getppid(),getpid());

            for( int j=0;j<2;++j ){// 跟外面那个for差不多(很暴力)
                pid[j] = fork();
                if( pid[j]==0 ){// 第三层了,可以结束了
                    printf("My parent is %d, my pid is %d.\n",getppid(),getpid());
                    printf("Process %d had exited.\n",getpid());
                    exit(0);
                }else if( pid[j]!=-1 ){// 父进程
                    printf("Parent process %d create child process %d.\n",getpid(),pid[j]);
                }else{
                    printf("Error: when %d fork()!\n",getpid());
                }
            }

            // waitpid,给孩子收尸,免得孩子变成僵尸进程
            for( int j=0;j<2;++j ){
                waitpid(pid[j],NULL,0);
            }
            printf("Process %d had exited.\n",getpid());

            exit(0);
        }else if( pid[i]!=-1 ){
            printf("Parent process %d create child process %d.\n",getpid(),pid[i]);
        }else{
            printf("Error: when %d fork()!\n",getpid());
        }
    }

    // waitpid,给孩子收尸,免得孩子变成僵尸进程
    for( int i=0;i<2;++i ){
        waitpid(pid[i],NULL,0);
    }

    printf("Process %d had exited.\n",getpid());

    exit(0);
}

怎么样,是不是真的很暴力。
代码中之所以可以每一层都是用pid[]这个数组去存子进程的pid,是因为使用fork()之后,子进程会复制父进程的代码、变量的值(只是数值而已,不是同一块内存)等,并从fork()语句下面开始执行。此时,子进程的数据跟父进程的数据在内存里已经不是同一块地址了,也就是说,在子进程中改变变量的值,不会对父进程产生影响。(不考虑实现了共享内存之类的东西哈)

上面代码有个很骚的地方,就是如果要求实现100层的进程树的话,那就要写99个for,骚气十足。(内心OS:鬼会有这么无聊的要求哦。。。)但是呢,作为一个准程序员,还是要想一波优化的啦,所以就想出了下面的这种写法。


2. 不写死层数的做法

刚才也说了嘛,fork()之后,子进程会复制父进程的数据,所以我们可以利用这一点,来实现终止条件的控制。
首先,可以用一个变量例如叫做layer(本英语渣每次取变量名都要搜一波英语单词怎么拼)来表示当前已经搞定几层了。因为一开始的进程可以看作是第一层,树的根节点嘛,所以layer初始赋值为1。之后在循环中,每fork()一层子进程,就在该进程中将当前的层数变量layer加一。由于fork()之后,子进程会复制父进程的数据,故此时在子进程中,layer的值与父进程是相同的。例如,根节点fork()出来的两个子进程的layer变量都是2,很符合要求。在子进程中判断layer是否已达到目标层数,如果是,则打印信息说一下byebye,使用exit(0)自杀就行了。如果不是,则修改循环的下标,继续从头开始fork()子进程。最后,在循环结束后,调用waitpid()收尸,防止子进程变成僵尸进程,结束程序。
感觉需要看一波代码,才能知道上面那堆东西在说什么Orz

代码如下:

#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>

int main(){
    pid_t pid[2];
    int layer = 1;
    const int targetLayer = 3;// 目标层数

    printf("Root pid is %d.\n",getpid());
    for( int i=0;i<2; ){// 这里没有++i哦
        if( i==0 ) layer++;// 条件控制,使得整个循环中layer只增加1
        pid[i] = fork();

        if( pid[i]==0 ){// 子进程
            printf("My parent is %d, my pid is %d.\n",getppid(),getpid());

            if( layer>=targetLayer ){// 层数够了,结束
                printf("Process %d had exited.\n",getpid());
                exit(0);
            }else{
                i = 0;// 从头开始循环,以便fork两次
            }
        }else if( pid[i]!=-1 ){// 父进程
            printf("Parent process %d create child process %d.\n",getpid(),pid[i++]);//下标+1
        }else{
            printf("Error: when %d fork()!\n",getpid());
        }
    }

    for( int i=0;i<2;++i ){
        waitpid(pid[i],NULL,0);
    }

    printf("Process %d had exited.\n",getpid());

    exit(0);
}

上面的代码只需要修改targetLayer的值即可控制要弄多少层。懒得每次都输入,就直接写个常量了
怎么样,对比之下是不是显得代码精炼了很多咧→_→


第一次在网上贴代码,有点瑟瑟发抖。如果诸位大佬发现有bug,欢迎指出一起探讨~~


猜你喜欢

转载自blog.csdn.net/feng964497595/article/details/79672214