守護進程 是脫離控制終端、在背景長期運行的進程。PHP 透過 pcntl_fork() 與 posix_setsid() 可實現標準的雙重 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');
}
?>
/dev/null 或日誌檔# /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 更可靠。
flock()) 或 Systemd 防止重複啟動pcntl_signal_dispatch() 或使用 declare(ticks=1) (PHP 5.3+)