| 類型 | 成因 | 後果 | 解決 |
| 僵屍 | 子進程先退出,父進程未 wait | 佔用 PID、內核 task_struct,大量則耗盡 PID | 父進程調用 wait/waitpid,或父進程先死由 init 收割 |
| 孤兒 | 父進程先退出,子進程還在運行 | 被 init (PID 1) 收養,正常退出由 init wait | 通常無害,守護進程常利用此特性脫離終端 |
五、exec() 族:換殼不換魂
int execl(const char *path, const char *arg0, ..., (char *)NULL);
int execv(const char *path, char *const argv[]);
int execlp(const char *file, const char *arg0, ..., (char *)NULL); // 搜索 PATH
// l=列表參數,v=向量參數,p=搜索 PATH,e=指定環境變量
- 本質:將調用進程的整個地址空間(代碼、數據、堆、棧)替換為新程序,但保留 PID、父子關係、打開的文件描述符(除非設定
FD_CLOEXEC)。
- 不返回:成功則不返回,失敗返回 -1(如文件不存在、權限不足、非可執行文件)。
- 緩衝區:exec 前必須
fflush(NULL),否則舊緩衝區數據隨地址空間丟失。
六、經典組合:fork + exec + wait = 簡易 Shell
pid_t pid = fork();
if (pid == 0) { // 子進程
execl("/bin/date", "date", "+%s", NULL);
perror("execl"); // 只有失敗才執行到這裡
_exit(1); // 子進程失敗用 _exit 避免衝刷父緩衝區
}
wait(NULL); // 父進程等待子進程
puts("End!");
七、system() 內部實現原理
int system(const char *cmd) {
pid_t pid = fork();
if (pid == 0) {
execl("/bin/sh", "sh", "-c", cmd, NULL);
_exit(127);
}
waitpid(pid, NULL, 0);
return 0; // 簡化:實際需處理信號、返回值解析
}
八、進程憑證:Real/Effective/Saved UID/GID
- Real:真實身份(登錄用戶)。
- Effective:權限檢查時使用的身份。
- Saved:保存執行文件所有者 ID,支持權限切換。
Set-UID 程序:如 passwd (-rwsr-xr-x),執行瞬間 Effective UID = 文件所有者,獲得 root 權限修改 /etc/shadow。
九、解釋器腳本與 #! 機制
#!/bin/bash:內核識別 #!,執行 /bin/bash script.sh。
- 腳本權限需
+x;解釋器必須是二進制可執行文件。
- 內核
execve 直接處理,不經過 Shell。
徹底掌握 fork/exec/wait 三部曲、僵屍孤兒機制、COW 原理、exec 族選擇、Set-UID 權限模型,是 Unix/Linux 系統編程的入門鑰匙。
- 上一篇文章: ACdream 暴力專場題解:蘋果分配問題的十重循環暴力破解
- 下一篇文章: C編譯器後端核心:匯編代碼生成中的寄存器分配與管理策略
Copyright ©
程式碼之家 All Rights Reserved