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

PHP PCNTL 核心函數詳解與多進程實戰範例

     編輯:PHP 相關知識

PHP PCNTL 核心函數詳解與多進程實戰範例

PHP 的 pcntl 擴展(需編譯時啟用 --enable-pcntl 及 posix)提供了進程創建、信號處理、程序替換等系統級能力。本文逐一講解常用函數並給出完整實戰範例。

常用 PCNTL 函數速查

  1. pcntl_alarm(seconds):設定秒數後發送 SIGALRM 信號。
  2. pcntl_signal(signo, handler):為指定信號註冊回調函數。配合 declare(ticks=1) 實現異步信號處理。
  3. pcntl_exec(path, args, envs):在當前進程空間執行外部程式,類似 C 的 exec 族函數,執行後當前進程結束。
  4. pcntl_fork():創建子進程。父進程得子進程 PID,子進程得 0。建議緊跟判斷邏輯,子進程區塊以 exit 結束。
  5. pcntl_wait(&status):阻塞等待任一子進程結束,返回其 PID 並將狀態寫入 $status。
  6. pcntl_waitpid(pid, &status, options):等待指定 PID 子進程。options=WNOHANG 可非阻塞輪詢。
  7. pcntl_getpriority / pcntl_setpriority:獲取/設定進程優先級(nice 值)。
  8. posix_kill(pid, signo):向進程發送信號。

信號處理機制與 Ticks

PCNTL 使用 declare(ticks=N) 定義每執行 N 條底層語句檢查一次信號隊列。範例:每 5 秒觸發 SIGALRM 並打印:

declare(ticks = 1);
function signal_handler($signal) {
    print "Caught SIGALRM\n";
    pcntl_alarm(5);
}
pcntl_signal(SIGALRM, "signal_handler", true);
pcntl_alarm(5);
for(;;) { } // 保持進程運行

子進程退出狀態解析

pcntl_waitpid 返回的 $status 可配合以下函數分析:

  • pcntl_wifexited:是否正常退出
  • pcntl_wexitstatus:取得退出碼
  • pcntl_wifsignaled:是否因信號終止
  • pcntl_wtermsig:終止信號編號

完整多進程任務分發類(Signfork)

以下類別封裝了進程池管理、任務分發、結果收集與僵屍進程清理:

class Signfork {
    private $tmp_path = '/tmp/';
    public function run($obj, $arg=1) {
        if (!method_exists($obj, '__fork')) exit("Method '__fork' not found!");
        // 解析參數並創建子進程
        // ...
        return $this->request($spawns);
    }
    private function spawn($obj, $i, $param=null) {
        if (pcntl_fork() === 0) {
            $cid = getmypid();
            file_put_contents($this->tmp_path.'sfpid_'.$cid, $obj->__fork($param));
            posix_kill($cid, SIGTERM);
            exit;
        }
    }
    private function request($spawns) {
        $data = [];
        for ($ids=0; $ids<$i; $ids++) {
            while (!($cid=pcntl_waitpid(-1,$status,WNOHANG))) usleep(30000);
            $tmpfile = $this->tmp_path.'sfpid_'.$cid;
            $data[] = file_get_contents($tmpfile);
            unlink($tmpfile);
        }
        return $data;
    }
}

雙重 Fork 技巧避免僵屍進程

父進程 fork 子進程 → 子進程 fork 孫進程並退出 → 孫進程被 init 進程收養 → 孫進程結束由 init 自動回收。PHP 實現:

$pid = pcntl_fork();
if ($pid > 0) { sleep(5); exit(0); } // 父進程退出
else {
    $gpid = pcntl_fork();
    if ($gpid > 0) { pcntl_wait($status); exit(0); } // 子進程退出
    else {
        // 孫進程執行核心任務
        posix_kill(getmypid(), SIGTERM);
        exit(0);
    }
}

常見問題與解決方案

  • 僵屍進程:子進程退出後父進程未回收。預防:pcntl_signal(SIGCHLD, SIG_IGN) 或雙重 fork 或定時 pcntl_waitpid(-1, $status, WNOHANG) 輪詢。
  • 子進程感知父進程退出:getppid()==1 表示父進程已變為 init;或 kill($ppid, 0)==-1 檢測父進程是否存在。
  • 後台長期運行:使用 nohup php script.php & 或 screen 避免終端關閉導致進程終止。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved