進程生命週期總覽
- 啟動:
int main(int argc, char *argv[]),argv 存放命令列參數,環境變數可用全域 environ 存取。
- 運行中:
fork() 建立子進程(複製地址空間),vfork() 共享地址空間(極速但危險),exec() 族替換程式映像(PID 不變),system() 執行 Shell 命令(注意 setuid 權限傳遞風險)。
- 結束:正常
exit()/return(刷新 I/O 緩衝),異常 _exit()/_Exit()(直接返回核心,不刷緩衝)。
核心系統呼叫與知識點
1. atexit() 註冊結束處理器
採堆疊順序(後進先出)執行:
#include <apue.h>
static void funcA_1() { printf("funcA_1\n"); }
static void funcB_2() { printf("funcB_2\n"); }
static void funcA_3() { printf("funcA_3\n"); }
int main() {
atexit(funcA_1);
atexit(funcB_2);
atexit(funcA_3);
exit(0);
}
// 輸出順序:funcA_3 → funcB_2 → funcA_1
2. 進程記憶體佈局四區
- 初始化資料段:全域/靜態且有初值(
int a = 1;)。
- 未初始化資料段 (BSS):全域/靜態無初值,核心自動清零。
- 堆疊:自動變數、函數呼叫框架、返回位址。
- 堆積:
malloc/free 動態分配。
3. 進程 ID 與關係
getpid() 取得自身 PID,getppid() 取得父進程 PID。
- PID 0:排程器;PID 1:
init 進程(所有孤兒進程的最終繼承者)。
- 父進程先結束 → 子進程成為孤兒 →
init 收養(init 會 wait 所有子進程,不產生殭屍)。
4. fork() 與 vfork() 差異
| 特性 | fork() | vfork() |
| 地址空間 | 複製(COW) | 共享 |
| 執行順序 | 不確定(race condition) | 子進程先跑,父進程阻塞至子進程 exec/exit |
| 用途 | 一般建立子進程 | 緊接 exec 時的效能優化 |
5. wait()/waitpid() 防殭屍
#include <apue.h>
int main() {
pid_t pid = fork();
if (pid < 0) err_sys("fork error");
else if (pid == 0) { // child
exit(1);
}
sleep(2); // 父進程故意不 wait
system("ps -o pid,ppid,state,tty,command");
exit(0);
}
// ps 輸出會出現 Z 狀態進程 → 殭屍
父進程必須 wait 回收子進程退出狀態,否則子進程進入 Z 狀態佔用 PID 資源。
6. exec() 族函數
- 成功不返回,失敗返回 -1;原程式
exec 後指令不再執行。
execl("./prog", "prog", "arg1", (char*)0):argv[0] 慣例為程式名,易誤用為第一個參數,建議佔位後從 argv[1] 開始使用。
7. system() 安全性陷阱
絕對不要在 setuid/setgid 程式中呼叫 system()!它會啟動 Shell,繼承提升後的權限,導致任意命令執行漏洞。
APUE 精華筆記摘錄
exit() = 清理緩衝 + 關閉標準流 + 返回核心;_exit() 直接返回核心。
return n 在 main 中等同 exit(n)。
- 核心唯一透過
exec 族啟動新程式。
- 堆疊儲存自動變數與呼叫框架。
- 進程只能影響自身及子進程環境,無法影響父進程(通常是 Shell)。
- PID 0 為核心排程器,無磁碟映像;PID 1 為
init,擁有超級權限的用戶進程。
fork() 呼叫一次、返回兩次(父/子各一次)。
- 子進程獲得父進程資料段、堆積、堆疊的副本,共享程式碼段。
sizeof(array) 回傳整個陣列位元組數(含 \\0);sizeof(ptr) 回傳指標大小;strlen 不含 \\0。
- 標準輸出連接終端機時為行緩衝,否則全緩衝。
- 所有開啟檔案描述符在
fork 時複製;共享 file table 即共享 current file offset。
- 進程終止時核心統一關閉描述符、釋放記憶體。
- 異常終止由核心產生退出狀態;核心以
SIGCHLD 通知父進程。
wait() 唯一錯誤為「無子進程」;EINTR 需重試。
fork 建立進程,exec 載入新程式。
ARG_MAX ≥ 4096 bytes(參數+環境變數總長)。
- 檔案描述符
close-on-exec 旗標:置位則 exec 時自動關閉。
- 唯有超級用戶進程可修改 Real UID。
scanf/printf 在標準流關閉時返回 -1。
- UID 0 = root。
- 上一篇文章: STL 容器深度剖析:hash_map 實現原理與效能權衡
- 下一篇文章: VC++ 對話框資料交換 (DDX/DDV) 機制深度解析
Copyright ©
程式碼之家 All Rights Reserved