Linux之fork與vfork區別

創建一個新進程的方法只有由某個已存在的進程調用fork()或vfork()

wKiom1ePL3ez9NYFAAAHVJ_16P4891.png

1.fork()函數

wKioL1ePL5vxnPw7AAAepqgYMns615.png

返回值:成功:父進程:返回子進程的PID

                       子進程:返回0

             失敗:父進程返回-1

子進程是父進程的一個拷貝。即子進程從父進程得到數據段和堆、棧段的拷貝,這些需要分配新的內存(不是與父進程共享,而是單獨分配內存);而對於只讀的代碼段,通常使用共享內存的方式訪問。

fork返回後,子進程和父進程都從調用fork函數的下一條語句開始執行。

由於子進程與父進程的運行是無關的,所以,父進程可先於子進程運行,子進程也可以先於父進程運行

eg:

myfork.c

wKioL1ePL9DCNzNVAACFfsEw0aY947.png

Makefile

wKioL1ePL_bRlFViAAAZrZQKSKM084.png

運行結果

wKiom1ePMBfCyuxCAAA64raeeZ8302.png

以前的fork創建一個子進程時,將會創建一個新的地址空間,並且拷貝父進程的資源,然後將會有兩種行爲:1.執行從父進程那裏拷貝過來的代碼段(進程希望複製自身,從而父子進程能同時執行不同段的代碼);2. 調用exec執行一個新的代碼段(進程想執行另外一個程序)

當進程調用exec時,一個進程替換了當前進程的文本、數據、棧、堆段。這樣,前面的拷貝工作就白費力氣了,這種情況下,人們想出了vfork。

vfork並不複製父進程的進程環境,子進程在父進程的地址空間中運行,所以子進程不能進行寫操作,並且兒子“霸佔”着父親的房子的時候,就要委屈父親一下,讓他在外面歇着(阻塞),一旦兒子執行了exec或者exit後,相當於兒子買了屬於自己的房子,這時候就相當於分家了

2.vfork()函數

wKiom1ePMIzgEAaVAAAoV9Kz-TI441.png

vfork創建新進程的主要目的在於調用exec函數執行另外的一個新程序,在沒調用exec或exit之前,子進程的運行是與父進程共享數據段的。

vfork調用中,子進程先運行,父進程掛起,直到子進程調用exec或者exit,在這以後,父子進程的執行順序不再被限制。

eg:

myvfork.c

wKioL1ePMLehki8JAACBKtfjyKo084.png

Makefile

wKioL1ePMNmQxfe4AAAbxuOhsUM358.png

運行結果

wKioL1ePMQWh0QwcAAAuxVr3icY081.png


一個經典的例子

test.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
void test()
{
    pid_t pid;
    pid=vfork();
    if(pid<0)  //失敗
    {
        printf("vfork error\n");
        exit(0);
    }
    else if(pid==0)
    {
        printf("1:child pid=%d,ppid=%d\n",getpid(),getppid());
        return;  //return執行完後,把控制權交給調用函數,而exit()執行完後把控制權交給系統 ,改成_exit(0),則會有另一種結果
    }
    else
    {
        printf("2:parent pid=%d,ppid=%d\n",getpid(),getppid());
    }
}
void fun()
{
    int i=0;
    int buf[100];
    for(;i<100;i++)
    {
        buf[i]=0;
    }
    printf("3:child pid=%d,ppid=%d\n",getpid(),getppid());
}
int main()
{
    test();
    fun();
    return 0;
}

Makefile

wKiom1ePMWbQrdpWAAAVUET55ZY671.png

運行結果

wKiom1ePMYWj1AUrAAA0ClronaU917.png


程序在後續執行時出現了錯誤,並且可知道是在父進程中出現的錯誤

vfork函數調用時,子進程比父進程先運行,在調用test()函數執行時,子進程執行完之後,將清理test函數的棧空間,然後子進程再調用fun()函數,將覆蓋掉test的棧空間,繼續執行fun函數。但是,當子進程退出後,執行父進程,但是,在test函數返回的時候該棧空間已經被子進程破壞了,不存在了,所以就出現了棧錯誤

區別:

1.vfork保證子進程先運行,在它調用exec或者exit之後,父進程纔可能被調度運行,之後,父子進程的執行順序纔不再有限制。如果在調用exec或者exit之前,子進程依賴於父進程的進一步動作,則會導致死鎖

2.fork要拷貝父進程的進程環境(數據段),而vfork不需要完全拷貝父進程的進程環境(數據段),在調用exec或者exit之前,父子進程共享進程環境(數據段),相當於線程概念,此時父進程阻塞等待(因爲子進程先運行)。

結束子進程:

exit和_exit函數用於正常終止一個程序,_exit()立即進入內核,exit()則需要先執行一些清除處理(包括調用執行各終止處理程序,關閉所有標準I/O流等),然後再進入內核。

結束子進程不用exit(0),而使用_exit(0)。因爲_exit(0)在結束進程時,不對標準I/O流進行任何操作,而exit(0)回關閉進程的所有標準I/O流


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