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

Linux 多進程開發(三):守護進程完整實現指南——從理論到生產級代碼

     編輯:關於C語言

守護進程是 Linux 服務器編程的基石(如 httpd、sshd、crond)。本文系統講解守護進程七大核心特性,提供完整可生產使用的 C 代碼實現,並演示 syslog 日誌配置與運行驗證全流程。

一、 守護進程七大核心特性(標準化流程)

  1. 後台運行:fork() 產生子進程,父進程退出 → 子進程脫離終端控制
  2. 新會話組長:setsid() 創建新會話 → 成為會話組長、進程組長、脫離控制終端
  3. 禁止重開終端:再次 fork(),父進程退出 → 非會話組長,無法重新獲取控制終端
  4. 關閉文件描述符:遍歷關閉 0~NOFILE 所有繼承描述符 → 釋放資源、避免文件系統卸載失敗
  5. 切換工作目錄:chdir("/") → 防止掛載文件系統無法卸載
  6. 重置文件創建掩碼:umask(0) → 確保創建文件權限不受限制
  7. 處理 SIGCHLD:signal(SIGCHLD, SIG_IGN) → 避免子進程變殭屍進程佔用資源

二、 完整生產級代碼實現

#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <time.h>
#include <syslog.h>
#include <stdio.h>

int init_daemon(void) {
    int pid, i;

    // 1. 忽略終端相關信號
    signal(SIGTTOU, SIG_IGN);
    signal(SIGTTIN, SIG_IGN);
    signal(SIGTSTP, SIG_IGN);
    signal(SIGHUP, SIG_IGN);

    // 2. 第一次 fork,父進程退出
    pid = fork();
    if (pid > 0) exit(0);
    if (pid < 0) return -1;

    // 3. 創建新會話,脫離控制終端
    setsid();

    // 4. 第二次 fork,確保非會話組長,禁止重開終端
    pid = fork();
    if (pid > 0) exit(0);
    if (pid < 0) return -1;

    // 5. 關閉所有繼承的文件描述符
    for (i = 0; i < NOFILE; close(i++)) ;

    // 6. 切換工作目錄到根目錄
    chdir("/");

    // 7. 重置文件創建掩碼
    umask(0);

    // 8. 忽略 SIGCHLD,防止殭屍進程
    signal(SIGCHLD, SIG_IGN);

    return 0;
}

int main(void) {
    time_t now;
    init_daemon();
    
    // 使用 syslog 記錄日誌
    openlog("TestDaemon", LOG_PID, LOG_USER);
    syslog(LOG_INFO, "Test daemon started");
    
    while (1) {
        sleep(8);
        time(&now);
        syslog(LOG_INFO, "System time: %s", ctime(&now));
    }
    
    closelog();
    return 0;
}

三、 系統配置:啟用 syslog 用戶日誌

1. 編輯 /etc/syslog.conf(或 rsyslog.conf)

# 在文件末尾添加
user.*    /var/log/test.log

2. 重啟 syslog 服務

/etc/init.d/syslog stop
/etc/init.d/syslog start
# 或 systemd 系統
systemctl restart rsyslog

四、 運行驗證與結果演示

1. 進程狀態驗證

$ ps -ef | grep test_daemon
root     12345     1  0 14:30 ?        00:00:00 ./test_daemon

ps -ef 查看守護進程

  • PPID = 1(被 init 進程收養)
  • 終端欄位為 ?(無控制終端)
  • STAT 通常為 S 或 Ss(會話組長)

2. 日誌輸出驗證

日誌文件內容

$ tail -f /var/log/test.log
Jun 18 14:30:01 localhost TestDaemon[12345]: Test daemon started
Jun 18 14:30:09 localhost TestDaemon[12345]: System time: Tue Jun 18 14:30:09 2013
Jun 18 14:30:17 localhost TestDaemon[12345]: System time: Tue Jun 18 14:30:17 2013

五、 現代 Linux 系統的改進建議

  1. 使用 daemon(3) 库函數:BSD 擴展,一行代碼完成上述大部分工作(但不可移植)
  2. systemd 服務單元:現代發行版首選 systemd 管理守護進程,無需雙重 fork、setsid,直接編寫 .service 文件
  3. 信號處理升級:使用 sigaction() 替代 signal(),設置 SA_RESTART、SA_NOCLDWAIT
  4. 文件描述符關閉優化:使用 closefrom() (BSD) 或 /proc/self/fd 遍歷關閉
  5. PID 文件管理:啟動時寫入 PID 文件,支持單實例、優雅停止、狀態查詢

六、 經典守護進程模板(現代風格)

#include <systemd/sd-daemon.h>  // systemd 通知支持

int main() {
    // 1. 初始化:解析配置、打開日誌、創建監聽套接字等
    
    // 2. 通知 systemd 就緒
    sd_notify(0, "READY=1");
    
    // 3. 主事件循環
    while (running) {
        // 處理請求、定時任務等
        sd_notify(0, "WATCHDOG=1");  // 看門狗心跳
    }
    
    // 4. 清理資源
    return 0;
}

本文出自 “_Liang_Happy_Life__Dream” 博客,轉載請保留出處:原文鏈接

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved