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

PHP 實現守護進程:posix_setsid() 與雙重 Fork 完整實作

     編輯:PHP 相關知識

PHP 守護進程:脫離終端、在背景持續運行的標準實作

守護進程 是脫離控制終端、在背景長期運行的進程。PHP 透過 pcntl_fork() 與 posix_setsid() 可實現標準的雙重 Fork 守護進程模式,適用於常駐服務、佇列消費者、定時任務等場景。

標準雙重 Fork 實作

<?php
// 第一次 Fork:脫離父進程控制
$pid = pcntl_fork();

if ($pid < 0) {
    exit("Fork 失敗\n");
} elseif ($pid > 0) {
    // 父進程:直接退出,讓子進程被 init (PID 1) 收養
    exit(0);
}

// ===== 第一層子進程 (Session Leader) =====
// 建立新會話,脫離控制終端
$sid = posix_setsid();
if ($sid < 0) {
    exit("建立會話失敗\n");
}

// 第二次 Fork:確保進程不再獲得控制終端
$pid2 = pcntl_fork();
if ($pid2 < 0) {
    exit("第二次 Fork 失敗\n");
} elseif ($pid2 > 0) {
    // 第一層子進程退出
    exit(0);
}

// ===== 第二層子進程 (真正的守護進程) =====
// 變更工作目錄至根目錄,避免掛載點無法卸載
chdir('/');

// 關閉標準輸入/輸出/錯誤 (可選:重導向至日誌檔)
fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);

// 設定檔案建立遮罩
umask(0);

// ===== 核心業務邏輯 =====
$running = true;
pcntl_signal(SIGTERM, function() use (&$running) { $running = false; });

while ($running) {
    // 執行定期任務
    doTask();
    
    // 休眠 5 秒
    sleep(5);
}

function doTask(): void {
    // 實際業務:資料庫清理、郵件發送、快取預熱等
    // 建議記錄日誌至檔案:error_log("Task executed at " . date('c'), 3, '/var/log/mydaemon.log');
}
?>

關鍵步驟解析

  1. 第一次 Fork + 父進程退出:子進程成為孤兒進程,被 init 進程 (PID 1) 收養,脫離原終端控制
  2. posix_setsid():建立新會話,進程成為會話領導者,失去控制終端
  3. 第二次 Fork:防止會話領導者重新獲得控制終端 (某些系統允許會話領導者打開終端)
  4. chdir('/'):避免進程鎖定掛載目錄導致無法卸載檔案系統
  5. 關閉標準 I/O:釋放檔案描述符,防止輸出干擾;生產環境建議重導向至 /dev/null 或日誌檔
  6. umask(0):確保建立檔案權限不受遮罩限制

進階:Systemd 整合 (現代 Linux 推薦)

# /etc/systemd/system/my-php-daemon.service
[Unit]
Description=My PHP Daemon
After=network.target

[Service]
Type=simple
User=www-data
ExecStart=/usr/bin/php /var/www/daemon.php
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

使用 Systemd 管理守護進程可獲得自動重啟、日誌集中、依賴管理、開機自啟等企業級功能,比傳統雙重 Fork 更可靠。

常見問題排查

  • 進程意外退出:檢查 PHP 錯誤日誌、記憶體限制、資料庫連線超時
  • 多實例衝突:使用 PID 檔鎖定 (flock()) 或 Systemd 防止重複啟動
  • 信號處理不生效:需在循環中呼叫 pcntl_signal_dispatch() 或使用 declare(ticks=1) (PHP 5.3+)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved