本文章基于 pcntl 擴展做的多進程測試。
進程調(diào)度策略
父子進程的調(diào)度由操作系統(tǒng)來負責,具體先調(diào)度子進程還是父進程由系統(tǒng)的調(diào)度算法決定,當然可以在父進程加上延時或是調(diào)用進程回收函數(shù) pcntl_wait 可以先讓子進程先運行,進程回收的目的是釋放進程創(chuàng)建時占用的內(nèi)存空間,防止變成僵尸進程。
信號:
信號稱為軟中斷系統(tǒng)或是叫軟中斷,功能是向進程發(fā)送異步事件通知。
信號編號: 【源碼基于 SIGINT,SIGTERM,SIGUSR1 信號,含義請自行查看 kill 命令手冊,不在描述】
linux 支持 64 個,有一半為實時信號,一半為非時實信號,這些信號都有自己的編號和對應的整數(shù)值。每個信號的編號含義讀者可以參閱 linux 相關手冊【man 手冊看看就知道了】
信號處理函數(shù):
信號一般會綁定相應的功能,有的是默認動作如 SIGKILL,SIGTERM,SIGINT 操作默認操作就是干掉進程,當然我們可以重寫覆蓋掉,就是通過 pcntl_signal 來覆蓋掉。
信號的概念:與硬件中斷一個道理,請讀者自行參考本人前面擼過的文章或是查看芯片硬件中斷原理。
信號的發(fā)送:
kill 信號編號 進程 或是按鍵產(chǎn)品的中斷信號或是在源碼里可以使用 posix_kill 等函數(shù)。
進程是相互隔離的,擁有自己的堆??臻g,除了一些公用的正文【代碼區(qū)】,同時也有自己的可執(zhí)行代碼,進程運行時,將占用 cpu 的資源,其它進程將無權(quán)運行,此時其它進程將為阻塞狀態(tài)【比如前面擼過的 tcp 服務】,當進程運行結(jié)束后【運行到代碼的最后一句或是遇到 return 或是遇到 exit 退出進程函數(shù)或是遇到信號事件時將會退出】讓出權(quán)限并釋放掉內(nèi)存,其它進程就有機會運行了。
進程擁有的自己進程描述符,其中比較常用的是進程號 PID,進程運行時會在系統(tǒng) /proc/PID 下生成相應的進程文件,用戶可以自行查看。
每個進程都擁有所屬的進程組【進程的集合】,多個進程組集合則是一個會話,創(chuàng)建一個會話是通過一個進程進行創(chuàng)建的,并且此進程不可以為組長進程,此進程將成為會話期的會話首進程,也會成為進程組的進程組長,同時將會脫離控制終端,即使之前的進程綁定了控制終端也會脫離【守護進程的創(chuàng)建】。
文件描述權(quán)限掩碼【權(quán)限屏蔽字】:
umask () 你可以在 linux 運行這個命令,然后創(chuàng)建文件,并查看它的權(quán)限【如果你跑完啥也沒有發(fā)現(xiàn),說明你還是訓練不夠 ^_^】
?php
/**
* Created by PhpStorm.
* User: 1655664358@qq.com
* Date: 2018/3/26
* Time: 14:19
*/
namespace Chen\Worker;
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("服務未運行\(zhòng)n");
}
}
function setMasterPid()
{
$fp = fopen($this->master_pid_file,"w");
@fwrite($fp,posix_getpid());
@fclose($fp);
}
function daemon()
{
$pid = pcntl_fork();
if ($pid0){
exit("fork進程失敗\n");
}else if ($pid >0){
exit(0);
}else{
umask(0);
$sid = posix_setsid();
if ($sid0){
exit("創(chuàng)建會話失敗\n");
}
$pid = pcntl_fork();
if ($pid0){
exit("進程創(chuàng)建失敗\n");
}else if ($pid >0){
exit(0);
}
//可以關閉標準輸入輸出錯誤文件描述符【守護進程不需要】
}
}
function worker()
{
if (count($this->workerJob)==0)exit("沒有工作任務\n");
foreach($this->workerJob as $job){
$pid = pcntl_fork();
if ($pid0){
exit("工作進程創(chuàng)建失敗\n");
}else if ($pid==0){
/***************子進程工作范圍**********************/
//給子進程安裝信號處理程序
$this->workerInstallSignal();
$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()."接受了父進程發(fā)來的自定義信號\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);//向所有的子進程發(fā)出
}
exit("服務平滑停止\n");
break;
case SIGTERM://ctrl+z
foreach ($this->workerPids as $pid=>$worker){
posix_kill($pid,SIGKILL);//向所有的子進程發(fā)出
}
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 pid\n".$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 Chen\Worker;
class Job
{
public $job_run_time = 3600;
function run($pid)
{\sleep(3);
echo "worker pid = $pid job 沒事干,就在這里job\n";
}
}
?php
/**
* Created by PhpStorm.\ * User: 1655664358@qq.com
* Date: 2018/3/26\ * Time: 14:37\ */\namespace Chen\Worker;
class Talk
{
public $job_run_time = 3600;
function run($pid)
{\sleep(3);
echo "worker pid = $pid job 沒事干,就在這里talk\n";
}
}
?php
/**
* Created by PhpStorm.\ * User: 1655664358@qq.com
* Date: 2018/3/26\ * Time: 15:45\ */
require_once 'vendor/autoload.php';
$process = new \Chen\Worker\Server();
$process->workerJob = [new \Chen\Worker\Talk(),new \Chen\Worker\Job()];
$process->run();

以上就是PHP 多進程與信號中斷實現(xiàn)多任務常駐內(nèi)存管理【Master/Worker 模型】的詳細內(nèi)容,感謝大家的學習和對腳本之家的支持。
您可能感興趣的文章:- php多進程中的阻塞與非阻塞操作實例分析
- php多進程并發(fā)編程防止出現(xiàn)僵尸進程的方法分析
- php 的多進程操作實踐案例分析
- php 多進程編程父進程的阻塞與非阻塞實例分析
- php實現(xiàn)的簡單多進程服務器類完整示例
- PHP多進程簡單實例小結(jié)
- php swoole多進程/多線程用法示例【基于php7nts版】
- PHP基于swoole多進程操作示例
- 詳解PHP多進程消費隊列