首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Linux 系統編程:從 Redis 持久化機制理解子進程退出與僵屍進程處理

     編輯:關於C語言

背景:Redis 持久化與 fork 子進程

Redis 的 RDB 和 AOF 持久化均通過 fork() 創建子進程完成數據持久化。子進程完成任務後調用 exit() 退出,父進程必須正確回收子進程資源,否則會產生僵屍進程,最終耗盡系統 PID 資源。

進程退出流程

  1. 子進程調用 exit(status)(或 _exit())
  2. 用戶態清理:關閉打開的文件描述符、刪除臨時文件、釋放用戶態內存等
  3. 調用內核 _exit():釋放內核資源(頁表、文件表、信號處理等)
  4. 內核將退出狀態封裝為信號 SIGCHLD 發送給父進程
  5. 子進程進入「僵屍」狀態,保留 task_struct 等最少內核結構,等待父進程讀取退出信息

父進程回收僵屍進程的關鍵函數

wait()

pid_t wait(int *status);
  • 阻塞等待任意一個子進程退出
  • 返回退出子進程的 PID,status 存儲退出信息
  • 只能處理一個子進程,多個子進程同時退出時可能遺漏

waitpid() —— 推薦使用

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 實踐啟示

Redis 在 rdbSaveBackground() 和 rewriteAppendOnlyFileBackground() 中 fork() 子進程後,父進程註冊 SIGCHLD 處理器,在事件循環中定期調用 waitpid(-1, &stat, WNOHANG) 回收子進程。這正是生產級網絡服務處理子進程的標準範式。

常見誤區

  • 忽略 SIGCHLD 或設置 SIG_IGN:雖然 Linux 允許忽略自動回收,但不利於獲取子進程退出碼進行業務邏輯判斷
  • 僅調用一次 wait():高併發場景下會遺漏子進程,導致僵屍累積
  • 忘記 WNOHANG:父進程主循環被阻塞,無法處理網絡請求
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved