vfork( ):
上節(jié)學(xué)習(xí)了fork( )時(shí)的寫(xiě)時(shí)復(fù)制機(jī)制筷黔,實(shí)際上在早期并沒(méi)有實(shí)現(xiàn)寫(xiě)時(shí)復(fù)制糕珊,在實(shí)現(xiàn)COW之前哄尔,Unix的設(shè)計(jì)者們就一直很關(guān)注在fork后立刻執(zhí)行exec所造成的地址空間的浪費(fèi)。
BSD的開(kāi)發(fā)者們?cè)?.0的BSD系統(tǒng)中引入了vfork( )系統(tǒng)調(diào)用:
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
- 除了子進(jìn)程必須要立刻執(zhí)行一次對(duì)exec的系統(tǒng)調(diào)用侍咱,或者調(diào)用_exit( )退出耐床,對(duì)vfork( )的成功調(diào)用所產(chǎn)生的結(jié)果和fork( )是一樣的。
- vfork( )會(huì)掛起父進(jìn)程直到子進(jìn)程終止或者運(yùn)行了一個(gè)新的可執(zhí)行文件的映像楔脯。通過(guò)這種方式撩轰,vfork( )避免了地址空間的按頁(yè)復(fù)制。
- 在這個(gè)過(guò)程中昧廷,父進(jìn)程和子進(jìn)程共享相同的地址空間和頁(yè)表項(xiàng)(不使用寫(xiě)時(shí)復(fù)制)堪嫂。實(shí)際上,vfork( )只完成了一件事:復(fù)制內(nèi)部的內(nèi)核數(shù)據(jù)結(jié)構(gòu)木柬。因此皆串,子進(jìn)程也就不能修改地址空間中的任何內(nèi)存。
長(zhǎng)度眉枕。
終止進(jìn)程
POSIX和C89都定義了終止當(dāng)前進(jìn)程的標(biāo)準(zhǔn)函數(shù):
#include <stdlib.h>
void exit(int status);
對(duì)exit( )的調(diào)用通常會(huì)執(zhí)行一些基本的終止進(jìn)程的步驟恶复,然后通知內(nèi)核終止這個(gè)進(jìn)程。
status參數(shù)用來(lái)標(biāo)識(shí)進(jìn)程退出的狀態(tài):
- EXIT_SUCCESS表示成功.
- EXIT_FAILURE表示失敗.
在Linux中速挑,0通常表示成功谤牡;非零值,例如1或者-1姥宝,表示失敗翅萤。
進(jìn)程成功的退出時(shí),只需要簡(jiǎn)單的寫(xiě)上:
exit(EXIT_SUCCESS);
在終止進(jìn)程之前腊满,C語(yǔ)言函數(shù)執(zhí)行以下關(guān)閉進(jìn)程的工作:
- 以在系統(tǒng)中注冊(cè)的逆序來(lái)調(diào)用由atexit( )或on_exit( )注冊(cè)的函數(shù)
- 清空所有已打開(kāi)的標(biāo)準(zhǔn)I/O流
- 刪除由tmpfile( )創(chuàng)建的所有臨時(shí)文件
這些步驟完成了在用戶空間中所需做的事情套么,這樣exit( )就可以調(diào)用_exit( )來(lái)讓內(nèi)核來(lái)處理終止進(jìn)程的剩余工作了:
#include <unistd.h>
void _exit(int status);
當(dāng)進(jìn)程退出時(shí),內(nèi)核會(huì)清理進(jìn)程所創(chuàng)建的碳蛋、不再用到的任何資源胚泌。這包括且不僅限于這些:申請(qǐng)的內(nèi)存、打開(kāi)的文件和system V的信號(hào)量疮蹦。
清理完成后,內(nèi)核摧毀進(jìn)程茸炒,并告知父進(jìn)程其子進(jìn)程的終止愕乎。
- 應(yīng)用程序可以直接調(diào)用_exit( )阵苇,但這通常是不合適的,但vfork( )的使用者終止進(jìn)程必須使用_exit( )感论,而不是exit( )绅项。
- ISO C99標(biāo)準(zhǔn)中增加了_Exit( )函數(shù),它的功能和_exit( )是一樣的:
#include <stdlib.h>
void _Exit(int status);
- 其他終止進(jìn)程的方式
- 典型方式:并非通過(guò)明確的使用一個(gè)系統(tǒng)調(diào)用比肄,而是采用跳轉(zhuǎn)到程序結(jié)尾處的方式快耿。(例如在main( )函數(shù)返回時(shí)明確給出一個(gè)狀態(tài)值,或者調(diào)用exit( )芳绩,成功時(shí)的返回是exit(0)掀亥, 或者是從main( )函數(shù)返回0)
- 如果進(jìn)程收到一個(gè)信號(hào),并且這個(gè)信號(hào)對(duì)應(yīng)的處理函數(shù)是終止進(jìn)程妥色,進(jìn)程也會(huì)終止搪花。這樣的信號(hào)包括SIGTERM和SIGKILL。
- 進(jìn)程被內(nèi)核懲罰性的終止嘹害。內(nèi)核就會(huì)殺死執(zhí)行非法指令撮竿,引起一個(gè)段錯(cuò)誤,或者內(nèi)存耗盡的進(jìn)程笔呀。
atexit( )
atexit( )用來(lái)注冊(cè)一些在進(jìn)程結(jié)束時(shí)要調(diào)用的函數(shù):
#include <stdlib.h>
int atexit (voidf (*function)(void));
對(duì)atexit( )的成功調(diào)用會(huì)把指定的函數(shù)注冊(cè)到進(jìn)程正常結(jié)束時(shí)調(diào)用的函數(shù)中幢踏。
- 如果進(jìn)程調(diào)用了exec,所注冊(cè)的函數(shù)列表會(huì)被清除(因?yàn)檫@些函數(shù)不存在于新進(jìn)程的地址空間中)
- 如果進(jìn)程是通過(guò)信號(hào)而結(jié)束的许师,這些注冊(cè)的函數(shù)也不會(huì)被調(diào)用房蝉。
要注冊(cè)的函數(shù)必須是無(wú)參且沒(méi)有返回值的,原型像這樣:
void my_function(void);
被注冊(cè)的函數(shù)以棧存儲(chǔ)枯跑,所以調(diào)用方式是FIFO的惨驶,先注冊(cè)的后調(diào)用。
- 注冊(cè)的函數(shù)不能調(diào)用exit( )敛助,否則會(huì)引起無(wú)限的遞歸調(diào)用粗卜。如果需要提前結(jié)束進(jìn)程,應(yīng)該調(diào)用_exit( )纳击,但是不推薦這樣做续扔。
- POSIX標(biāo)準(zhǔn)要求atexit( )至少支持注冊(cè)ATEXIT_MAX個(gè)函數(shù),而這個(gè)值至少是32焕数。具體的值可以通過(guò)sysconf( )得到纱昧,參數(shù)是_SC_ATEXIT_MAX。
一個(gè)簡(jiǎn)單的使用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( )成功時(shí)返回0,錯(cuò)誤時(shí)返回-1.
SIGCHLD
當(dāng)一個(gè)進(jìn)程子進(jìn)程終止時(shí)堡赔,內(nèi)核會(huì)向其父進(jìn)程發(fā)送SIGCHILD信號(hào)识脆。
缺省情況下,會(huì)忽略此信號(hào)量,父進(jìn)程也不會(huì)有任何的動(dòng)作灼捂。
進(jìn)程也可通過(guò)signal( )或者sigaction( )系統(tǒng)調(diào)用來(lái)有選擇的處理這個(gè)信號(hào)离例。
- 子進(jìn)程的終止和父進(jìn)程是異步的,所以SIGCHILD信號(hào)可能會(huì)在任何時(shí)候產(chǎn)生悉稠,也會(huì)在任何時(shí)候被傳遞給父進(jìn)程宫蛆。