Redis 的 RDB 和 AOF 持久化均通過 fork() 創建子進程完成數據持久化。子進程完成任務後調用 exit() 退出,父進程必須正確回收子進程資源,否則會產生僵屍進程,最終耗盡系統 PID 資源。
exit(status)(或 _exit())_exit():釋放內核資源(頁表、文件表、信號處理等)SIGCHLD 發送給父進程task_struct 等最少內核結構,等待父進程讀取退出信息pid_t wait(int *status);
status 存儲退出信息pid_t waitpid(pid_t pid, int *status, int options);
pid = -1:等待任意子進程pid > 0:等待指定 PID 的子進程options = WNOHANG:非阻塞模式,無子進程退出立即返回 0// 在 SIGCHLD 信號處理函數中
while (waitpid(-1, &status, WNOHANG) > 0) {
// 子進程已回收,可根據 status 進一步判斷退出原因
if (WIFEXITED(status)) printf("正常退出, code=%d\n", WEXITSTATUS(status));
if (WIFSIGNALED(status)) printf("被信號 %d 終止\n", WTERMSIG(status));
}
關鍵點:
WNOHANG 避免父進程阻塞while 循環處理「信號不排隊」問題:多個子進程同時退出只產生一個 SIGCHLD,循環可一次性回收所有WIFEXITED、WIFSIGNALED 等宏分析退出狀態Redis 在 rdbSaveBackground() 和 rewriteAppendOnlyFileBackground() 中 fork() 子進程後,父進程註冊 SIGCHLD 處理器,在事件循環中定期調用 waitpid(-1, &stat, WNOHANG) 回收子進程。這正是生產級網絡服務處理子進程的標準範式。
SIGCHLD 或設置 SIG_IGN:雖然 Linux 允許忽略自動回收,但不利於獲取子進程退出碼進行業務邏輯判斷wait():高併發場景下會遺漏子進程,導致僵屍累積WNOHANG:父進程主循環被阻塞,無法處理網絡請求