函數原型
#include
#include
pid_t fork(void);
核心機制
- 調用一次,返回兩次:父進程返回子進程 PID (>0);子進程返回 0;出錯返回 -1 (父進程)。
- Copy-on-Write (COW):現代內核不立即複製物理頁面,而是標記頁面只讀,父/子共享物理內存;任一方嘗試寫入時觸發缺頁中斷,內核複製該頁。
- 子進程繼承:文件描述符表、信號處理、環境變量、當前工作目錄、用戶/組 ID、內存映射 (COW)、打開文件偏移量 (共享文件表項)。
- 子進程差異:獨立 PID、PPID、資源統計清零、未決信號清空、文件鎖不繼承、定時器不繼承。
經典示例
#include
#include
#include
#include
int main(void) {
pid_t pid;
char *message;
int n;
pid = fork();
if (pid < 0) {
perror("fork failed");
exit(1);
}
if (pid == 0) { // 子進程
message = "This is the child\n";
n = 3;
} else { // 父進程
message = "This is the parent\n";
n = 6;
}
for (; n > 0; n--) {
printf("%s", message);
sleep(1);
}
return 0;
}
運行結果 (交替輸出,順序不定)
This is the parent
This is the child
This is the parent
This is the child
This is the parent
This is the child
This is the parent
This is the parent
This is the parent
關鍵點解析
- 返回值用途:父進程獲取子 PID 以便
wait/waitpid 回收、發送信號;子進程可調用 getppid() 獲取父 PID。
- 並發執行:
fork 後父子並發,調度器決定執行順序,輸出交替。
- 緩衝區問題:
printf 行緩衝 (終端) 或全緩衝 (重定向文件);fork 複製用戶空間緩衝區,若子進程退出前未刷新,父子可能重複輸出緩衝內容。最佳實踐:fork 前 fflush(stdout) 或使用無緩衝 I/O。
- 僵屍進程:子進程退出、父進程未
wait 回收 → 僵屍。長期運行服務必須安裝 SIGCHLD 處理器調用 waitpid(-1, NULL, WNOHANG)。
常見錯誤與最佳實踐
| 錯誤 | 後果 | 修正 |
| 忽略 fork 返回值 -1 | 資源耗盡時邏輯失效 | 必須檢查錯誤並處理 |
| 父子進程同時操作共享文件偏移量 | 數據交織、覆蓋 | 使用 pread/pwrite 或協調鎖 |
| 子進程調用 exit() 而非 _exit() | 刷新 stdio 緩衝區導致雙重輸出 | 子進程錯誤退出用 _exit(),正常返回 main 用 return |
| 未處理 SIGCHLD 導致僵屍堆積 | PID 耗盡、系統負載升高 | 設置 SA_NOCLDWAIT 或信號處理器回收 |