| 特性 | exit(int status) | _exit(int status) / _Exit(int status) |
| 歸屬 | C 標準庫 (<stdlib.h>) | POSIX 系統調用 / C11 (<stdlib.h>) |
| 標準 I/O 緩衝區 | 刷新 所有 stdio 流 | 不刷新 |
atexit() 註冊函數 | 逆序調用 | 不調用 |
| C++ 靜態對象析構函數 | 調用 | 不調用 |
臨時文件 (tmpfile) | 刪除 | 不刪除 |
| 返回到內核 | 最終調用 _exit() 系統調用 | 直接進入內核 |
經典誤用場景與後果
1. fork() 子進程調用 exit()
// 錯誤示範
if (fork() == 0) {
// 子進程工作...
exit(0); // 危險!
}
// 父進程繼續...
後果:子進程 exit() 刷新 stdio 緩衝區 → 數據寫入文件描述符;父進程稍後也 exit() 再次刷新同一緩衝區 → **數據重複寫入**。臨時文件被子進程刪除,父進程嘗試再刪除報錯。C++ 靜態對象析構兩次 → 雙重釋放崩潰。
2. vfork() 子進程調用 exit()
vfork() 子進程共享父進程地址空間,子進程 exit() 執行清理會**破壞父進程狀態**(緩衝區、鎖、堆元數據),導致父進程後續運行崩潰或邏輯錯誤。**必須用 _exit()**。
3. 守護進程雙重 Fork 模式
if (fork() != 0) _exit(0); // 父進程直接 _exit,避免清理干擾子進程
setsid();
if (fork() != 0) _exit(0); // 二代父進程 _exit
// 孫進程成為守護進程...
此處父進程必須用 _exit(),否則標準輸出緩衝區刷新可能導致控制終端掛起。
正確選擇決策樹
- **主程序
main 返回 / 正常結束** → 隱式調用 exit()(或显式 return),完成完整清理。
- **
fork() 子進程** 正常退出 → _exit()。
- **
vfork() 子進程** 任何退出 → _exit()。
- **嚴重錯誤需立即終止**(如
exec 失敗後) → _exit() 避免清理副作用。
- **C++ 程序中
std::quick_exit** (C++11) → 僅調用 at_quick_exit 處理器,不析構靜態對象,介於兩者之間。
標準依據
- C99/C11:
exit 執行 atexit、刷新流、調用 _Exit;_Exit 直接返回宿主環境。
- POSIX.1-2008:
_exit 系統調用關閉所有打開文件描述符、釋放內存、發送 SIGCHLD 給父進程。
金句:exit() 是「體面告別」,_exit() 是「悄無聲息離場」。子進程、嚴重錯誤、守護進程選後者;主程序正常結束選前者。
- 上一篇文章: Linux 系統編程:fcntl 函數族完整詳解(文件描述符標志、狀態標志、異步 I/O、記錄鎖)
- 下一篇文章: Objective-C 數據類型自動轉換與強制轉換完整指南
Copyright ©
程式碼之家 All Rights Reserved