vfork( ):
上節(jié)學(xué)習(xí)了fork( )時的寫時復(fù)制機制,實際上在早期并沒有實現(xiàn)寫時復(fù)制,在實現(xiàn)COW之前,Unix的設(shè)計者們就一直很關(guān)注在fork后立刻執(zhí)行exec所造成的地址空間的浪費。
BSD的開發(fā)者們在3.0的BSD系統(tǒng)中引入了vfork( )系統(tǒng)調(diào)用:
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
- 除了子進程必須要立刻執(zhí)行一次對exec的系統(tǒng)調(diào)用,或者調(diào)用_exit( )退出,對vfork( )的成功調(diào)用所產(chǎn)生的結(jié)果和fork( )是一樣的。
- vfork( )會掛起父進程直到子進程終止或者運行了一個新的可執(zhí)行文件的映像。通過這種方式,vfork( )避免了地址空間的按頁復(fù)制。
- 在這個過程中,父進程和子進程共享相同的地址空間和頁表項(不使用寫時復(fù)制)。實際上,vfork( )只完成了一件事:復(fù)制內(nèi)部的內(nèi)核數(shù)據(jù)結(jié)構(gòu)。因此,子進程也就不能修改地址空間中的任何內(nèi)存。
長度。
終止進程
POSIX和C89都定義了終止當(dāng)前進程的標(biāo)準(zhǔn)函數(shù):
#include <stdlib.h>
void exit(int status);
對exit( )的調(diào)用通常會執(zhí)行一些基本的終止進程的步驟,然后通知內(nèi)核終止這個進程。
status參數(shù)用來標(biāo)識進程退出的狀態(tài):
- EXIT_SUCCESS表示成功.
- EXIT_FAILURE表示失敗.
在Linux中,0通常表示成功;非零值,例如1或者-1,表示失敗。
進程成功的退出時,只需要簡單的寫上:
exit(EXIT_SUCCESS);
在終止進程之前,C語言函數(shù)執(zhí)行以下關(guān)閉進程的工作:
- 以在系統(tǒng)中注冊的逆序來調(diào)用由atexit( )或on_exit( )注冊的函數(shù)
- 清空所有已打開的標(biāo)準(zhǔn)I/O流
- 刪除由tmpfile( )創(chuàng)建的所有臨時文件
這些步驟完成了在用戶空間中所需做的事情,這樣exit( )就可以調(diào)用_exit( )來讓內(nèi)核來處理終止進程的剩余工作了:
#include <unistd.h>
void _exit(int status);
當(dāng)進程退出時,內(nèi)核會清理進程所創(chuàng)建的、不再用到的任何資源。這包括且不僅限于這些:申請的內(nèi)存、打開的文件和system V的信號量。
清理完成后,內(nèi)核摧毀進程,并告知父進程其子進程的終止。
- 應(yīng)用程序可以直接調(diào)用_exit( ),但這通常是不合適的,但vfork( )的使用者終止進程必須使用_exit( ),而不是exit( )。
- ISO C99標(biāo)準(zhǔn)中增加了_Exit( )函數(shù),它的功能和_exit( )是一樣的:
#include <stdlib.h>
void _Exit(int status);
- 其他終止進程的方式
- 典型方式:并非通過明確的使用一個系統(tǒng)調(diào)用,而是采用跳轉(zhuǎn)到程序結(jié)尾處的方式。(例如在main( )函數(shù)返回時明確給出一個狀態(tài)值,或者調(diào)用exit( ),成功時的返回是exit(0), 或者是從main( )函數(shù)返回0)
- 如果進程收到一個信號,并且這個信號對應(yīng)的處理函數(shù)是終止進程,進程也會終止。這樣的信號包括SIGTERM和SIGKILL。
- 進程被內(nèi)核懲罰性的終止。內(nèi)核就會殺死執(zhí)行非法指令,引起一個段錯誤,或者內(nèi)存耗盡的進程。
atexit( )
atexit( )用來注冊一些在進程結(jié)束時要調(diào)用的函數(shù):
#include <stdlib.h>
int atexit (voidf (*function)(void));
對atexit( )的成功調(diào)用會把指定的函數(shù)注冊到進程正常結(jié)束時調(diào)用的函數(shù)中。
- 如果進程調(diào)用了exec,所注冊的函數(shù)列表會被清除(因為這些函數(shù)不存在于新進程的地址空間中)
- 如果進程是通過信號而結(jié)束的,這些注冊的函數(shù)也不會被調(diào)用。
要注冊的函數(shù)必須是無參且沒有返回值的,原型像這樣:
void my_function(void);
被注冊的函數(shù)以棧存儲,所以調(diào)用方式是FIFO的,先注冊的后調(diào)用。
- 注冊的函數(shù)不能調(diào)用exit( ),否則會引起無限的遞歸調(diào)用。如果需要提前結(jié)束進程,應(yīng)該調(diào)用_exit( ),但是不推薦這樣做。
- POSIX標(biāo)準(zhǔn)要求atexit( )至少支持注冊ATEXIT_MAX個函數(shù),而這個值至少是32。具體的值可以通過sysconf( )得到,參數(shù)是_SC_ATEXIT_MAX。
一個簡單的使用atexit( )的例子:
#include <stdio.h>
#include <stdlib.h>
void out(void) {
printf("atexit( ) succeeded! \n");
}
int main(void) {
if (atexit(out))
fprintf(stderr, "atexit( ) failed! \n");
return 0;
}
atexit( )成功時返回0,錯誤時返回-1.
SIGCHLD
當(dāng)一個進程子進程終止時,內(nèi)核會向其父進程發(fā)送SIGCHILD信號。
缺省情況下,會忽略此信號量,父進程也不會有任何的動作。
進程也可通過signal( )或者sigaction( )系統(tǒng)調(diào)用來有選擇的處理這個信號。
- 子進程的終止和父進程是異步的,所以SIGCHILD信號可能會在任何時候產(chǎn)生,也會在任何時候被傳遞給父進程。