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

PHP 編寫 Daemon 進程實戰:雙重 Fork 脫離終端、Socket HTTP Server 完整實現

     編輯:PHP 綜合知識

前言

PHP 不僅能寫 Web 腳本,通過 PCNTL + POSIX + Sockets 可編寫系統級 Daemon 服務。本文實現標準雙重 Fork 脫離終端,並構建一個極簡 HTTP Server,展示 PHP 服務端編程全貌。

Daemon 標準化流程(雙重 Fork)

  1. 第一次 Fork:父進程退出,子進程成為孤兒進程,被 init (PID 1) 收養,脫離控制終端。
  2. setsid():子進程創建新會話,成為會話首進程,失去控制終端。
  3. 第二次 Fork:會話首進程再次 Fork,孫進程不再是會話首進程,**永遠無法重新獲得控制終端**,徹底守護進程化。
  4. 孫進程執行業務邏輯,父/子進程相繼退出。

完整代碼:PHP HTTP Daemon Server


 */

// 1. 業務邏輯:處理 HTTP 請求
function handleHttpRequest(string $address, int $port): void {
    $maxBacklog = 16;
    $response = "HTTP/1.1 200 OK\r\n"
              . "Content-Length: 15\r\n"
              . "Content-Type: text/plain; charset=UTF-8\r\n"
              . "\r\n"
              . "PHP HTTP Server";
    $responseLen = strlen($response);

    // 創建 Socket
    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    if ($socket === false) {
        die("socket_create failed: " . socket_strerror(socket_last_error()) . "\n");
    }

    // 端口復用(重啟快速佔用)
    socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);

    // 綁定
    if (socket_bind($socket, $address, $port) === false) {
        die("socket_bind failed: " . socket_strerror(socket_last_error()) . "\n");
    }

    // 監聽
    if (socket_listen($socket, $maxBacklog) === false) {
        die("socket_listen failed: " . socket_strerror(socket_last_error()) . "\n");
    }

    echo "HTTP Server listening on {$address}:{$port}\n";

    // 事件循環
    while (true) {
        $client = socket_accept($socket);
        if ($client === false) {
            usleep(10000); // 短暫休眠
            continue;
        }

        // 讀取請求(簡單讀取,生產需解析完整 HTTP)
        $request = socket_read($client, 8192);
        
        // 發送響應
        socket_write($client, $response, $responseLen);
        socket_close($client);
    }
}

// 2. Daemon 化封裝
function daemonize(): void {
    // 第一次 Fork
    $pid1 = pcntl_fork();
    if ($pid1 === -1) die("First fork failed\n");
    if ($pid1 > 0) exit(0); // 父進程退出

    // 子進程:創建新會話
    if (posix_setsid() === -1) die("posix_setsid failed\n");

    // 第二次 Fork
    $pid2 = pcntl_fork();
    if ($pid2 === -1) die("Second fork failed\n");
    if ($pid2 > 0) exit(0); // 第一子進程退出

    // 孫進程:真正的 Daemon
    // 可選:切換工作目錄、umask、關閉標準流
    chdir('/');
    umask(0);
    fclose(STDIN);
    fclose(STDOUT);
    fclose(STDERR);
    // 重定向到 /dev/null 或日誌文件
    $null = fopen('/dev/null', 'r');
    $log = fopen('/var/log/php_daemon.log', 'a');
    $err = fopen('/var/log/php_daemon_error.log', 'a');
    // 注意:fopen 返回的資源 ID 不一定是 0/1/2,需用 dup2 (PHP 無原生 dup2)
    // 簡單做法:不關閉 STDIN/OUT/ERR,或用 proc_open 處理
}

// 3. 入口
daemonize();
handleHttpRequest('0.0.0.0', 9999);

關鍵技術點解析

技術點函數/常量作用
創建進程pcntl_fork()複製進程空間,返回 PID
創建會話posix_setsid()脫離控制終端,成為會話首領
信號處理pcntl_signal()捕獲 SIGTERM/SIGINT 實現優雅關閉
Socket 創建socket_create(AF_INET, SOCK_STREAM, SOL_TCP)TCP IPv4 Socket
端口復用socket_set_option(..., SO_REUSEADDR, 1)避免 TIME_WAIT 導致重啟失敗
非阻塞接受socket_accept() + 輪詢 / socket_select()單進程處理多連接(生產用 Swoole/ReactPHP)

生產級 Daemon 增強清單

  1. PID 文件管理:啟動寫入 /var/run/php_daemon.pid,停止讀取 PID 發送信號,防止多實例。
  2. 日誌輪轉:用 Monolog + RotatingFileHandler,或 logrotate 系統工具。
  3. 配置文件:YAML/JSON 配置端口、進程數、業務參數,啟動讀取。
  4. 優雅關閉:捕獲 SIGTERM → 停止接受新連接 → 處理完當前請求 → 關閉 Socket → 退出。
  5. 多進程 Worker主進程 Fork 多個 Worker 進程,Socket 由主進程 accept 後傳遞文件描述符(SCM_RIGHTS),或用 Swoole 內置多進程模型。
  6. 健康檢查:暴露 /health 接口,返回進程狀態、內存、隊列積壓等指標。
  7. Systemd 服務單元:/etc/systemd/system/php-daemon.service,標準化管理啟動/重啟/日誌。

現代替代方案:Swoole / OpenSwoole

set([
    'worker_num' => 4,
    'daemonize' => true,        // 內置 daemonize
    'pid_file' => '/var/run/swoole_http.pid',
    'log_file' => '/var/log/swoole_http.log',
]);
$server->on('Request', function ($request, $response) {
    $response->header("Content-Type", "text/plain");
    $response->end("Swoole HTTP Server");
});
$server->start();

優勢:內置多進程/協程、靜態文件服務、SSL、WebSocket、熱重載、監控指標,**生產環境首選**。

結語

PHP 編寫 Daemon 核心是雙重 Fork + setsid + 信號處理 + Socket 事件循環。理解這套機制有助於深入掌握進程模型,但**生產環境請直接使用 Swoole/OpenSwoole/Workerman** 等成熟框架,避免重複造輪子並獲得企業級穩定性。

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