操作系統 fork與exec

前言

學習操作系統,首先便要學到process概念。process是什麼?Process – a program in execution。七十年代UNIX最先提出多進程的構想,之後該構想便廣泛用於linux與unix操作系統中。fork()函數給程序猿們提供了簡便的多進程編程方式。

fork() creates new process

exec() used after a fork to replace the process’ memory space with a new program

下面就來看看fork()函數和exec()究竟幹了什麼。

fork()

fork() 創建一個新的進程,該進程與主進程爲父子關係。

Linux下一個進程在內存裏有三部分的數據,就是"代碼段"、"堆棧段"和"數據段"。

"代碼段",顧名思義,就是存放了程序代碼的數據,假如機器中有數個進程運行相同的一個程序,那麼它們就可以使用相同的代碼段。"堆棧段"存放的就是子程序的返回地址、子程序的參數以及程序的局部變量。而數據段則存放程序的全局變量,常數以及動態數據分配的數據空間(比如用malloc之類的函數取得的空間)。這其中有許多細節問題,這裏限於篇幅就不多介紹了。系統如果同時運行數個相同的程序,它們之間就不能使用同一個堆棧段和數據段。  

fork() 創建的子進程完全拷貝主進程,所以子進程的運行完全與主進程相同,且與主進程並行處理,競爭CPU的資源。

不同的是,主進程與子進程的fork()返回值不同,子進程中fork()的返回值是0,而主進程的fork()返回值是產生的子進程的進程id。

舉個栗子:

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(){
    int i;
    pid_t pid;

    //for another process
    pid  = fork();
    
    if(pid < 0){ //error
        printf("Fork failed");
        return 1;
    }

    else if(pid == 0){
        for(i = 0; i < 1000; i++){
            printf("This is a child\n");
        }
    }
    else{
        for(i = 0; i < 1000; i++){
            printf("process\n");
        }
    }
    return 0;
}

這樣運行時,就會交替輸出"This is a child"  與 "process", 可以看出子進程與主進程的並行處理。

這樣的競爭狀況是我們不希望看到的,所以下面修改一下

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(){
    int i;
    pid_t pid;

    //for another process
    pid  = fork();
    
    if(pid < 0){ //error
        printf("Fork failed");
        return 1;
    }

    else if(pid == 0){
        printf("This is a child\n");
    }
    else{
        wait(NULL);
        printf("Child complete\n");
    }
    return 0;
}

這樣主進程會等到子進程運行完了再運行。

exec()

fork產生一個進程,該進程如果只是運行主進程的程序,那如果我們希望這個進程去幹其他的活動,比如所我們在打字的時候同時聽着歌,怎麼辦呢?這裏就用到了exec()。

一個進程一旦調用exec類函數,它本身就"死亡"了,系統把代碼段替換成新的程序的代碼,廢棄原有的數據段和堆棧段,併爲新程序分配新的數據段與堆棧段,唯一留下的,就是進程號,也就是說,對系統而言,還是同一個進程,不過已經是另一個程序了。(不過exec類函數中有的還允許繼承環境變量之類的信息。)

來看個栗子

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(){
    int i;
    pid_t pid;

    //for another process
    pid = fork();
    
    if(pid < 0){ //error
        printf("Fork failed");
        return 1;
    }

    else if(pid == 0){ //run program.c
        execl("./program", "program", NULL);
    }
    else{
        wait(NULL);
        printf("Child complete\n");
    }
    return 0;
}

其中program.c

#include <stdio.h>
#include <ctype.h>

int main(){
    int i;
    for(i = 0; i < 10; i++){
        printf("A new program\n");
    }
    return 0;
}

這裏子進程就是運行的program.c。

總結

在傳統的Unix、linux環境下,有兩個基本的操作用於創建和修改進程:函數fork( )用來創建一個新的進程,該進程幾乎是當前進程的一個完全拷貝;函數族exec( )用來啓動另外的進程以取代當前運行的進程。這些函數給程序猿的多進程編程提供了遍歷,也增加了CPU的利用率。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章