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

Linux 守護進程編程:fork、setsid、雙重 Fork 完整實現與解析

     編輯:Java 相關知識

守護進程核心步驟

  1. fork() 創建子進程,父進程退出 → 子進程成為孤兒進程,由 init 進程收養,脫離終端控制。
  2. 子進程調用 setsid() 創建新會話,成為會話組長、進程組長,失去控制終端(無法接收 SIGINT/SIGHUP)。
  3. (可選)二次 fork() 確保進程非會話組長,無法重新獲得控制終端(更嚴格的守護進程模式)。
  4. 修改工作目錄為 /,避免掛載點無法卸載。
  5. 重設文件創建掩碼 umask(0)。
  6. 關閉不需要的文件描述符(0,1,2 或更多),重定向至 /dev/null。

完整示例代碼(C++,Ubuntu 測試)

#include <iostream>
#include <unistd.h>
using namespace std;

void print_ids() {
    cout << "PID=" << getpid() << ", PGID=" << getpgid(0) << endl;
}

int main() {
    pid_t pid = fork();
    if (pid < 0) { cerr << "fork error" << endl; return 1; }
    if (pid > 0) {  // 父進程
        cout << "Parent exiting..." << endl;
        return 0;
    }

    // 子進程:第一次 fork 後
    cout << "--- 1st child ---" << endl; print_ids();

    if (setsid() < 0) { cerr << "setsid error" << endl; return 1; }
    cout << "--- after setsid ---" << endl; print_ids();

    // 可選:二次 fork
    pid_t pid2 = fork();
    if (pid2 < 0) { cerr << "2nd fork error" << endl; return 1; }
    if (pid2 > 0) return 0;  // 第一子進程退出

    // 第二子進程:真正的守護進程
    cout << "--- daemon (2nd child) ---" << endl; print_ids();

    chdir("/");
    umask(0);
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);

    // 守護進程主邏輯
    for (int i = 0; i < 5; ++i) {
        sleep(20);
        cout << "Daemon alive..." << endl; print_ids();
    }
    return 0;
}

關鍵點解析

  • setpgrp() vs setsid():setpgrp() 僅設置進程組 ID;setsid() 創建新會話、新進程組、脫離控制終端,功能更強,單獨使用即可。
  • 輸出重定向:守護進程關閉標準輸出後,cout 將失效。實際部署應使用 syslog、文件日誌(openlog/fopen)記錄運行狀態。
  • 信號處理:需捕獲 SIGTERM 優雅關閉,忽略 SIGHUP、SIGPIPE、SIGCHLD(或安裝處理器回收殭屍進程)。

現代替代方案

生產環境建議使用成熟的進程管理工具,避免手寫守護邏輯:

  • systemd:Type=simple 服務單元,自動守護、重啟、日誌收集。
  • Supervisor:Python 寫的進程監控工具,配置簡單,Web 界面管理。
  • Docker:容器本身即前台進程模型,配合 --restart=always 實現守護語義。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved