Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Linux 守護進程 (Daemon) 程式設計完整範例:雙重 Fork 脫離終端機、信號處理、自動啟動

編輯:Linux 教學

前言

守護進程是在背景長期運行、無控制終端機、通常隨系統啟動的服務型程式。本文提供標準的 C 語言實作範例,涵蓋雙重 Fork 脫離終端機、建立新會話、關閉檔案描述符、改變工作目錄、重設 umask、信號處理(重載配置/優雅退出)、PID 記錄與 /etc/rc.local 自動啟動。

核心初始化函式 init_daemon()

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

void init_daemon(void) {
    pid_t pid;
    int i;

    /* 第一次 fork:父進程退出,子進程成為背景進程 */
    if ((pid = fork()) > 0) exit(0);      // 父進程結束
    else if (pid < 0) exit(1);            // fork 失敗

    /* 子進程:建立新會話,脫離控制終端機,成為會話組長 */
    setsid();

    /* 第二次 fork:確保進程不再是會話組長,防止重新獲得控制終端機 */
    if ((pid = fork()) > 0) exit(0);
    else if (pid < 0) exit(1);

    /* 第二子進程:關閉所有繼承的檔案描述符 */
    for (i = 0; i < NOFILE; ++i) close(i);

    /* 改變工作目錄到根目錄,避免掛載點無法卸載 */
    chdir("/tmp");   // 或 "/"

    /* 重設檔案建立掩碼 */
    umask(0);
}

測試主程式 daemontest.c

#include <signal.h>
#include <time.h>

void init_daemon(void);
void signal_reload(int sig);   // SIGUSR1: 重載配置
void signal_handle(int sig);   // SIGUSR2: 優雅退出

int main(int argc, char **argv) {
    FILE *fp;
    time_t t;

    init_daemon();                    // 變身守護進程
    signal(SIGCHLD, SIG_IGN);         // 忽略子進程退出,避免殭屍進程
    signal(SIGUSR1, signal_reload);   // 註冊重載信號
    signal(SIGUSR2, signal_handle);   // 註冊退出信號

    while (1) {
        sleep(60);                    // 主迴圈:每分鐘記錄心跳
        if ((fp = fopen("test.log", "a")) != NULL) {
            t = time(0);
            fprintf(fp, "I'm here at %s\n", asctime(localtime(&t)));
            fclose(fp);
        }
    }
    return 0;
}

void signal_reload(int sig) {
    FILE *fp; time_t t;
    if ((fp = fopen("test.log", "a")) != NULL) {
        t = time(0);
        fprintf(fp, "I received signal(%d), reload all parameters at %s\n", sig, asctime(localtime(&t)));
        fclose(fp);
    }
    /* 實際重載配置邏輯 */
}

void signal_handle(int sig) {
    FILE *fp; time_t t;
    if ((fp = fopen("test.log", "a")) != NULL) {
        t = time(0);
        fprintf(fp, "I received signal(%d), exit at %s\n", sig, asctime(localtime(&t)));
        fclose(fp);
    }
    exit(0);
}

編譯與執行

gcc init_daemon.c daemontest.c -o mydaemon
./mydaemon

進程將在背景運行,每分鐘在 /tmp/test.log 寫入心跳(因 chdir("/tmp"))。

系統開機自動啟動

編輯 /etc/rc.d/rc.local(或 systemd 服務單元),加入:

su - username -c "/path/to/mydaemon"

以指定用戶身分啟動,避免 root 權限過大。

運行期管理

  • 查看 PID:ps -ef | grep mydaemon
  • 熱重載配置:kill -s SIGUSR1 <PID>
  • 優雅關閉:kill -s SIGUSR2 <PID>
  • 強制終止:kill -9 <PID>(最後手段)

進階補充:Systemd 服務單元範例 (現代發布版推薦)

[Unit]
Description=My Custom Daemon
After=network.target

[Service]
Type=forking
ExecStart=/path/to/mydaemon
User=username
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

存為 /etc/systemd/system/mydaemon.service,執行 systemctl enable --now mydaemon 即可納入系統服務管理,支援依賴、日誌整合、自動重啟等企業級特性。

關鍵點總結

  1. 雙重 Fork + setsid() 徹底脫離終端機。
  2. 關閉繼承的檔案描述符,釋放資源。
  3. chdir("/") 防止檔案系統無法卸載;umask(0) 確保建檔權限可控。
  4. 妥善處理 SIGCHLD 避免殭屍進程;自訂信號實現熱重載/優雅退出。
  5. 現代系統優先使用 Systemd 管理,僅在相容舊系統或極簡環境手寫 Daemon 化邏輯。
Copyright © Windows教程網 All Rights Reserved