PHP 多进程与信号中断实现多任务常驻内存管理实例方法


Posted in PHP onOctober 04, 2019

本文章基于 pcntl 扩展做的多进程测试。

进程调度策略

父子进程的调度由操作系统来负责,具体先调度子进程还是父进程由系统的调度算法决定,当然可以在父进程加上延时或是调用进程回收函数 pcntl_wait 可以先让子进程先运行,进程回收的目的是释放进程创建时占用的内存空间,防止变成僵尸进程。

信号:

信号称为软中断系统或是叫软中断,功能是向进程发送异步事件通知。

信号编号: 【源码基于 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 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("服务未运行\n");

  }

 }

 function setMasterPid()

 {

  $fp = fopen($this->master_pid_file,"w");

  @fwrite($fp,posix_getpid());

  @fclose($fp);

 }

 function daemon()

 {

  $pid = pcntl_fork();

  if ($pid<0){

   exit("fork进程失败\n");

  }else if ($pid >0){

   exit(0);

  }else{

   umask(0);

   $sid = posix_setsid();

   if ($sid<0){

    exit("创建会话失败\n");

   }

   $pid = pcntl_fork();

   if ($pid<0){

    exit("进程创建失败\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 ($pid<0){

    exit("工作进程创建失败\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()."接受了父进程发来的自定义信号\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 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 多进程与信号中断实现多任务常驻内存管理实例方法

以上就是PHP 多进程与信号中断实现多任务常驻内存管理【Master/Worker 模型】的详细内容,感谢大家的学习和对三水点靠木的支持。

PHP 相关文章推荐
php 远程图片保存到本地的函数类
Dec 08 PHP
ThinkPHP3.1.3版本新特性概述
Jun 19 PHP
Laravel 4 初级教程之安装及入门
Oct 30 PHP
php提示Warning:mysql_fetch_array() expects的解决方法
Dec 16 PHP
PHP调用.NET的WebService 简单实例
Mar 27 PHP
PHP也能干大事 随机函数
Apr 14 PHP
Zend Framework实现留言本分页功能(附demo源码下载)
Mar 22 PHP
Yii使用smsto短信接口的函数demo示例
Jul 13 PHP
PHP面向对象程序设计组合模式与装饰模式详解
Dec 02 PHP
PHP结合jquery ajax实现上传多张图片,并限制图片大小操作示例
Mar 01 PHP
tp5(thinkPHP5框架)captcha验证码配置及验证操作示例
May 28 PHP
php模式设计之观察者模式应用实例分析
Sep 25 PHP
使用laravel和ajax实现整个页面无刷新的操作方法
Oct 03 #PHP
laravel 实现登陆后返回登陆前的页面方法
Oct 03 #PHP
解决laravel-admin 自己新建页面里 js 需要刷新一次的问题
Oct 03 #PHP
基于laravel-admin 后台 列表标签背景的使用方法
Oct 03 #PHP
浅谈laravel-admin的sortable和orderby使用问题
Oct 03 #PHP
关于laravel后台模板laravel-admin select框的使用详解
Oct 03 #PHP
laravel-admin select框默认选中的方法
Oct 03 #PHP
You might like
用Socket发送电子邮件(利用需要验证的SMTP服务器)
2006/10/09 PHP
用PHP实现Ftp用户的在线管理的代码
2007/03/06 PHP
php 删除目录下N分钟前创建的所有文件的实现代码
2013/08/10 PHP
php bootstrap实现简单登录
2016/03/08 PHP
PHP控制反转(IOC)和依赖注入(DI)
2017/03/13 PHP
url 特殊字符 传递参数解决方法
2010/01/01 Javascript
目前流行的JavaScript库的介绍及对比
2013/09/29 Javascript
5秒后跳转到另一个页面的js代码
2013/10/12 Javascript
基于NodeJS的前后端分离的思考与实践(三)轻量级的接口配置建模框架
2014/09/26 NodeJs
ajax在兼容模式下失效的快速解决方法
2016/03/22 Javascript
简单理解vue中el、template、replace元素
2016/10/27 Javascript
网页挂马方式整理及详细介绍
2016/11/03 Javascript
jQuery实现的手风琴侧边菜单效果
2017/03/29 jQuery
JS实现动态添加DOM节点和事件的方法示例
2017/04/28 Javascript
iframe与主框架跨域相互访问实现方法
2017/09/14 Javascript
详解vue静态资源打包中的坑与解决方案
2018/02/05 Javascript
深入浅析vue-cli@3.0 使用及配置说明
2019/05/08 Javascript
微信小程序与webview交互实现支付功能
2019/06/07 Javascript
改进 JavaScript 和 Rust 的互操作性并深入认识 wasm-bindgen 组件
2019/07/13 Javascript
在Python中使用sort()方法进行排序的简单教程
2015/05/21 Python
Python的Django框架中forms表单类的使用方法详解
2016/06/21 Python
浅谈python函数之作用域(python3.5)
2017/10/27 Python
Python对象中__del__方法起作用的条件详解
2018/11/01 Python
Python实现将元组中的元素作为参数传入函数的操作
2020/06/05 Python
Python暴力破解Mysql数据的示例
2020/11/09 Python
AmazeUI 平滑滚动效果的示例代码
2020/08/20 HTML / CSS
Oroton中国官网:澳洲知名奢侈配饰品牌
2017/03/26 全球购物
韩国保养品、日本药妆购物网:小三美日
2018/12/30 全球购物
漫威玩具服装及周边商品官方购物网站:Marvel Shop
2019/05/11 全球购物
成教自我鉴定
2013/10/27 职场文书
物业经理自我鉴定
2014/03/03 职场文书
综合办公室主任岗位职责
2015/04/01 职场文书
学生检讨书怎么写
2015/05/07 职场文书
古诗之爱国古诗5首
2019/09/20 职场文书
Go中的条件语句Switch示例详解
2021/08/23 Golang
Win11快速关闭所有广告推荐
2022/04/19 数码科技