1. 程式人生 > >linux fork() 程序樹的兩種實現方法

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,歡迎指出一起探討~~