Linux中的fork與exec系列函數(shù)分析
fork 和 exec 的使用體現(xiàn)了 UNIX 的精髓,它提供了一種非常簡單的方式來啟動新的任務(wù)。注意這里“任務(wù)”一詞的使用,我刻意避免使用“進程”或“程序”這兩個術(shù)語:
進程是“執(zhí)行引擎”,是操作系統(tǒng)中能夠運行程序的實體;
程序是用于執(zhí)行相同任務(wù)的特定代碼片段。
你可以在不同的進程中運行同一個程序(例如交互式 shell)。
fork()的功能
考慮到這一點,fork 調(diào)用基本上會復(fù)制當(dāng)前進程,幾乎在各個方面都完全相同。并非所有內(nèi)容都會被復(fù)制(例如,某些實現(xiàn)中的資源限制),但其目的是創(chuàng)建盡可能接近的副本。
fork將創(chuàng)建新進程(也叫子進程),子進程會獲得不同的進程 ID (PID),并將創(chuàng)建它的進程(父進程)的 PID 作為其父進程 PID (PPID)。雖然兩個進程運行的是同一個程序,但是它們可以通過 fork 的返回值來區(qū)分——子進程返回 0,而父進程返回子進程的 PID。當(dāng)然,這一切都建立在 fork 調(diào)用成功的前提上——如果失敗,則不會創(chuàng)建子進程,父進程會返回錯誤碼。
exec()的功能
exec 調(diào)用本質(zhì)上是用一個新程序替換進程中整個當(dāng)前程序的方法。它會將新程序加載到當(dāng)前進程空間,并從入口點運行。
因此,fork 和 exec 通常按順序使用,以使新程序作為當(dāng)前進程的子進程運行。每當(dāng)你嘗試運行類似 find 的程序時,Shell 通常會執(zhí)行此操作——Shell 會 fork,然后子進程將 find 程序加載到內(nèi)存中,設(shè)置所有命令行參數(shù)、標(biāo)準(zhǔn) I/O 等等。
一些 UNIX 實現(xiàn)對 fork 進行了優(yōu)化,使用了所謂的“寫時復(fù)制”機制。這是一種技巧,可以延遲 fork 過程中對進程空間的復(fù)制,直到程序嘗試更改該空間中的某些內(nèi)容。這對于只使用 fork 而不使用 exec 的程序非常有用,因為它們不必復(fù)制整個進程空間。
exec 調(diào)用有很多種(execl、execle、execve 等等),但此處上下文中的 exec 指的是其中任何一個。
實例演示
下圖演示了典型的 fork/exec 操作,其中使用 bash shell 的 ls 命令列出目錄:
+--------+| pid=7 || ppid=4 || bash |+--------+ |
| calls fork
V
+--------+ +--------+| pid=7 | forks | pid=22 || ppid=4 | ----------> | ppid=7 || bash | | bash |+--------+ +--------+ | |
| waits for pid 22 | calls exec to run ls | to finish V
| +--------+ | | pid=22 |
| | ppid=7 |
| | ls |
V +--------+
+--------+ || pid=7 | | exits
| ppid=4 | <---------------+
| bash |
+--------+
|
| continues
V
評論