本文章基于 pcntl 擴展做的多進程測試。
進程調度策略
父子進程的調度由操作系統來負責,具體先調度子進程還是父進程由系統的調度算法決定,當然可以在父進程加上延時或是調用進程回收函數 pcntl_wait 可以先讓子進程先運行,進程回收的目的是釋放進程創建時占用的內存空間,防止變成僵尸進程。
信號:
信號稱為軟中斷系統或是叫軟中斷,功能是向進程發送異步事件通知。
立即學習“PHP免費學習筆記(深入)”;
信號編號: 【源碼基于 SIGINT,SIGTERM,SIGUSR1 信號,含義請自行查看 kill 命令手冊,不在描述】
linux 支持 64 個,有一半為實時信號,一半為非時實信號,這些信號都有自己的編號和對應的整數值。每個信號的編號含義讀者可以參閱 linux 相關手冊【man 手冊看看就知道了】
信號處理函數:
信號一般會綁定相應的功能,有的是默認動作如 SIGKILL,SIGTERM,SIGINT 操作默認操作就是干掉進程,當然我們可以重寫覆蓋掉,就是通過 pcntl_signal 來覆蓋掉。
信號的概念:與硬件中斷一個道理,請讀者自行參考本人前面擼過的文章或是查看芯片硬件中斷原理。
信號的發送:
kill 信號編號 進程 或是按鍵產品的中斷信號或是在源碼里可以使用 posix_kill 等函數。
進程是相互隔離的,擁有自己的堆棧空間,除了一些公用的正文【代碼區】,同時也有自己的可執行代碼,進程運行時,將占用 cpu 的資源,其它進程將無權運行,此時其它進程將為阻塞狀態【比如前面擼過的 tcp 服務】,當進程運行結束后【運行到代碼的最后一句或是遇到 return 或是遇到 exit 退出進程函數或是遇到信號事件時將會退出】讓出權限并釋放掉內存,其它進程就有機會運行了。
進程擁有的自己進程描述符,其中比較常用的是進程號 PID,進程運行時會在系統 /proc/PID 下生成相應的進程文件,用戶可以自行查看。
每個進程都擁有所屬的進程組【進程的集合】,多個進程組集合則是一個會話,創建一個會話是通過一個進程進行創建的,并且此進程不可以為組長進程,此進程將成為會話期的會話首進程,也會成為進程組的進程組長,同時將會脫離控制終端,即使之前的進程綁定了控制終端也會脫離【守護進程的創建】。
文件描述權限掩碼【權限屏蔽字】:
umask () 你可以在 linux 運行這個命令,然后創建文件,并查看它的權限【如果你跑完啥也沒有發現,說明你還是訓練不夠 ^_^】
<?php /** * Created by PhpStorm. * User: 1655664358@qq.com * Date: 2018/3/26 * Time: 14:19 */ namespace ChenWorker; class Server { public $workerPids = []; public $workerJob = []; public $master_pid_file = "master_pid"; public $state_file = "state_file.txt"; function run() { $this->daemon(); ????????$this->worker(); ????????$this->setMasterPid(); ????????$this->installSignal(); ????????$this->showState(); ????????$this->wait(); ????} ????function?wait() ????{ ????????while?(1){ ????????????pcntl_signal_dispatch(); ????????????$pid?=?pcntl_wait($status); ????????????if?($pid>0){ ????????????????unset($this->workerPids[$pid]); ????????????}else{ ????????????????if?(count($this->workerPids)==0){ ????????????????????exit(); ????????????????} ????????????} ????????????usleep(100000); ????????} ????} ????function?showState() ????{ ????????$state?=?"nMaster?信息n"; ????????$state.=str_pad("master?pid",25); ????????$state.=str_pad("worker?num",25); ????????$state.=str_pad("job?pid?list",10)."n"; ????????$state.=str_pad($this->getMasterPid(),25); ????????$state.=str_pad(count($this->workerPids),25); ????????$state.=str_pad(implode(",",array_keys($this->workerPids)),10); ????????echo?$state.PHP_EOL; ????} ????function?getMasterPid() ????{ ????????if?(file_exists($this->master_pid_file)){ ????????????return?file_get_contents($this->master_pid_file); ????????}else{ ????????????exit("服務未運行n"); ????????} ????} ????function?setMasterPid() ????{ ????????$fp?=?fopen($this->master_pid_file,"w"); ????????@fwrite($fp,posix_getpid()); ????????@fclose($fp); ????} ????function?daemon() ????{ ????????$pid?=?pcntl_fork(); ????????if?($pid0){ ????????????exit(0); ????????}else{ ????????????umask(0); ????????????$sid?=?posix_setsid(); ????????????if?($sid0){ ????????????????exit(0); ????????????} ????????????//可以關閉標準輸入輸出錯誤文件描述符【守護進程不需要】 ????????} ????} ????function?worker() ????{ ????????if?(count($this->workerJob)==0)exit("沒有工作任務n"); ????????foreach($this->workerJob?as?$job){ ????????????$pid?=?pcntl_fork(); ????????????if?($pidworkerInstallSignal(); ????????????????$start_time?=?time(); ????????????????while?(1){ ????????????????????pcntl_signal_dispatch(); ????????????????????if?((time()-$start_time)>=$job->job_run_time){ ????????????????????????break; ????????????????????} ????????????????????$job->run(posix_getpid()); ????????????????} ????????????????exit(0);//子進程運行完成后退出 ????????????????/***************子進程工作范圍**********************/ ????????????}else{ ????????????????$this->workerPids[$pid]?=?$job; ????????????} ????????} ????} ????function?workerInstallSignal() ????{ ????????pcntl_signal(SIGUSR1,[__CLASS__,'workerHandleSignal'],false); ????} ????function?workerHandleSignal($signal) ????{ ????????switch?($signal){ ????????????case?SIGUSR1: ????????????????$state?=?"worker?pid=".posix_getpid()."接受了父進程發來的自定義信號n"; ????????????????file_put_contents($this->state_file,$state,FILE_APPEND); ????????????????break; ????????} ????} ????function?installSignal() ????{ ????????pcntl_signal(SIGINT,[__CLASS__,'handleMasterSignal'],false); ????????pcntl_signal(SIGTERM,[__CLASS__,'handleMasterSignal'],false); ????????pcntl_signal(SIGUSR1,[__CLASS__,'handleMasterSignal'],false); ????} ????function?handleMasterSignal($signal) ????{ ????????switch?($signal){ ????????????case?SIGINT: ????????????????//主進程接受到中斷信號ctrl+c ????????????????foreach?($this->workerPids?as?$pid=>$worker){ ????????????????????posix_kill($pid,SIGINT);//向所有的子進程發出 ????????????????} ????????????????exit("服務平滑停止n"); ????????????????break; ????????????case?SIGTERM://ctrl+z ????????????????foreach?($this->workerPids?as?$pid=>$worker){ ????????????????????posix_kill($pid,SIGKILL);//向所有的子進程發出 ????????????????} ????????????????exit("服務停止n"); ????????????????break; ????????????case?SIGUSR1://用戶自定義信號 ????????????????if?(file_exists($this->state_file)){ ????????????????????unlink($this->state_file); ????????????????} ????????????????foreach?($this->workerPids?as?$pid=>$worker){ ????????????????????posix_kill($pid,SIGUSR1); ????????????????} ????????????????$state?=?"master?pidn".$this->getMasterPid()."n"; ????????????????while(!file_exists($this->state_file)){ ????????????????????sleep(1); ????????????????} ????????????????$state.=?file_get_contents($this->state_file); ????????????????echo?$state.PHP_EOL; ????????????????break; ????????} ????} }?? <?php /** * Created by PhpStorm. * User: 1655664358@qq.com * Date: 2018/3/26 * Time: 14:37 */namespace ChenWorker; class Job { public $job_run_time = 3600; function run($pid) {sleep(3); echo "worker pid = $pid job 沒事干,就在這里jobn"; } } <?php /** * Created by PhpStorm. * User: 1655664358@qq.com * Date: 2018/3/26 * Time: 14:37 */namespace ChenWorker; class Talk { public $job_run_time = 3600; function run($pid) {sleep(3); echo "worker pid = $pid job 沒事干,就在這里talkn"; } } <?php /** * Created by PhpStorm. * User: 1655664358@qq.com * Date: 2018/3/26 * Time: 15:45 */ require_once 'vendor/autoload.php'; $process = new ChenWorkerServer(); $process->workerJob?=?[new?ChenWorkerTalk(),new?ChenWorkerJob()]; $process->run();
更多laravel相關技術文章,請訪問Laravel框架入門教程欄目進行學習!