【Linux】進程的創建fork()和vfork()

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

1、fork()函數微笑

NAME

                fork  - create a child process

SYNOPSIS

                #include<unistd.h>

                pid_t fork(void);

一個現有進程可以調用fork創建一個新進程。

返回值:成功:子進程中返回0,父進程中返回子進程ID,

             失敗:父進程返回-1。

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

2)子進程和父進程的運行無關,沒有先後順序。在fork之後的父進程先執行還是子進程先執行是不確定的(取決於內核的調度法) 。可以決定誰先退出,一般都是讓子進程先退出。 

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

由於fork之後經常歸屬exec,所以現在很多實現並不執行一個父進程數據段、棧和堆的完全複製。作爲替代,使用了寫時複製(Copy-On-Write)技術。這些區域由父子進程共享,而且內核將他們的訪問權限改變爲只讀的。如果父子進程中的任一個試圖修改這些區域,則內核只爲修改區域的那塊內存製作一個副本。

下面的程序演示了fork函數,從中可以看出子進程對變量所作的改變並不去影響父進程中該變量的值。


以前的fork創建一個子進程時,將會創建一個新的地址空間,並且拷貝父進程的資源。

之後會有兩種行爲:

1)執行從父進程那裏拷貝過來的代碼段(進程希望複製自身,從而父子進程能同時執行不同段的代碼);

2)調用exec執行一個新的代碼段(進程想執行另外一個程序)

在fork之後處理的文件描述符有兩種常見的情況:
1)父進程等待子進程完成。在這種情況下,父進程無需對其描述符做任何處理。當子進程終止後,子進程對文件偏移量的修改已執行的更新。
2)父子進程各自執行不同的程序段。這種情況下,在fork之後,父子進程各自關閉他們不需要使用的文件描述符,這樣就不會干擾對方使用文件描述符。這種方法在網絡服務進程中經常使用。
父子進程之間的區別:
1)fork的返回值
2) 進程ID不同
3)具有不同的父進程ID
4) 子進程的tms_utime、tms_stime、tms_cutime及tms_ustime均被設置爲0
5)父進程設置的文件鎖不會被子進程繼承
6)子進程的未處理鬧鐘被清除
7)子進程的未處理信號集被設置爲空集
fork有下面兩種用法:
1)一個父進程希望複製自己,使父子進程同時執行不同的代碼段。例如,父進程等待客戶端請求,生成子進程來處理請求。
2) 一個進程要執行一個不同的程序。例如子進程從fork返回後,調用exec函數。
fork調用失敗的原因:
1)系統中有太多的進程
2)實際用戶的進程數超過了限制



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

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

2、vfork()函數微笑

NAME

                vfork  - create a child process and block parent    ------block(阻塞)

SYNOPSIS

                #include<unistd.h>

               pid_t vfork(void);

Vfork創建新進程的主要目的是調用exec函數執行另一個新進程,在沒有調用exec或exit之前,子進程的運行是與父進程共享數據段的。vfork調用,子進程先運行,父進程掛起,直到子進程調用exec或者exit,在此之後,父子進程的執行順序不再被限制。


可見子進程直接改變了父進程的變量值,因爲子進程在父進程的地址空間中運行

3、fork()和vfork()的區別微笑

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

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


經典例子


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


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