当前位置:首页 > PHP教程 > php高级应用 > 列表

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

发布:smiling 来源: PHP粉丝网  添加日期:2021-12-27 15:06:55 浏览: 评论:0 

本文章基于 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 运行这个命令,然后创建文件,并查看它的权限【如果你跑完啥也没有发现,说明你还是训练不够 ^_^】

  1.  
  2. /** 
  3.  
  4.  * Created by PhpStorm. 
  5.  
  6.  * User: 1655664358@qq.com 
  7.  
  8.  * Date: 2018/3/26 
  9.  
  10.  * Time: 14:19 
  11.  
  12.  */ 
  13.  
  14. namespace Chen\Worker; 
  15.  
  16. class Server 
  17.  
  18.  
  19.  public $workerPids = []; 
  20.  
  21.  public $workerJob = []; 
  22.  
  23.  public $master_pid_file = "master_pid"
  24.  
  25.  public $state_file = "state_file.txt"
  26.  
  27.  function run() 
  28.  
  29.  { 
  30.  
  31.   $this->daemon(); 
  32.  
  33.   $this->worker(); 
  34.  
  35.   $this->setMasterPid(); 
  36.  
  37.   $this->installSignal(); 
  38.  
  39.   $this->showState(); 
  40.  
  41.   $this->wait(); 
  42.  
  43.  } 
  44.  
  45.  function wait() 
  46.  
  47.  { 
  48.  
  49.   while (1){ 
  50.  
  51.    pcntl_signal_dispatch(); 
  52.  
  53.    $pid = pcntl_wait($status); 
  54.  
  55.    if ($pid>0){ 
  56.  
  57.     unset($this->workerPids[$pid]); 
  58.  
  59.    }else
  60.  
  61.     if (count($this->workerPids)==0){ 
  62.  
  63.      exit(); 
  64.  
  65.     } 
  66.  
  67.    } 
  68.  
  69.    usleep(100000); 
  70.  
  71.   } 
  72.  
  73.  } 
  74.  
  75.  function showState() 
  76.  
  77.  { 
  78.  
  79.   $state = "\nMaster 信息\n"
  80.  
  81.   $state.=str_pad("master pid",25); 
  82.  
  83.   $state.=str_pad("worker num",25); 
  84.  
  85.   $state.=str_pad("job pid list",10)."\n"
  86.  
  87.   $state.=str_pad($this->getMasterPid(),25); 
  88.  
  89.   $state.=str_pad(count($this->workerPids),25); 
  90.  
  91.   $state.=str_pad(implode(",",array_keys($this->workerPids)),10); 
  92.  
  93.   echo $state.PHP_EOL; 
  94.  
  95.  } 
  96.  
  97.  function getMasterPid() 
  98.  
  99.  { 
  100.  
  101.   if (file_exists($this->master_pid_file)){ 
  102.  
  103.    return file_get_contents($this->master_pid_file); 
  104.  
  105.   }else
  106.  
  107.    exit("服务未运行\n"); 
  108.  
  109.   } 
  110.  
  111.  } 
  112.  
  113.  function setMasterPid() 
  114.  
  115.  { 
  116.  
  117.   $fp = fopen($this->master_pid_file,"w"); 
  118.  
  119.   @fwrite($fp,posix_getpid()); 
  120.  
  121.   @fclose($fp); 
  122.  
  123.  } 
  124.  
  125.  function daemon() 
  126.  
  127.  { 
  128.  
  129.   $pid = pcntl_fork(); 
  130.  
  131.   if ($pid<0){ 
  132.  
  133.    exit("fork进程失败\n"); 
  134.  
  135.   }else if ($pid >0){ 
  136.  
  137.    exit(0); 
  138.  
  139.   }else
  140.  
  141.    umask(0); 
  142.  
  143.    $sid = posix_setsid(); 
  144.  
  145.    if ($sid<0){ 
  146.  
  147.     exit("创建会话失败\n"); 
  148.  
  149.    } 
  150.  
  151.    $pid = pcntl_fork(); 
  152.  
  153.    if ($pid<0){ 
  154.  
  155.     exit("进程创建失败\n"); 
  156.  
  157.    }else if ($pid >0){ 
  158.  
  159.     exit(0); 
  160.  
  161.    } 
  162.  
  163.    //可以关闭标准输入输出错误文件描述符【守护进程不需要】 
  164.  
  165.   } 
  166.  
  167.  } 
  168.  
  169.  function worker() 
  170.  
  171.  { 
  172.  
  173.   if (count($this->workerJob)==0)exit("没有工作任务\n"); 
  174.  
  175.   foreach($this->workerJob as $job){ 
  176.  
  177.    $pid = pcntl_fork(); 
  178.  
  179.    if ($pid<0){ 
  180.  
  181.     exit("工作进程创建失败\n"); 
  182.  
  183.    }else if ($pid==0){ 
  184.  
  185.     /***************子进程工作范围**********************/ 
  186.  
  187.     //给子进程安装信号处理程序 
  188.  
  189.     $this->workerInstallSignal(); 
  190.  
  191.     $start_time = time(); 
  192.  
  193.     while (1){ 
  194.  
  195.      pcntl_signal_dispatch(); 
  196.  
  197.      if ((time()-$start_time)>=$job->job_run_time){ 
  198.  
  199.       break
  200.  
  201.      } 
  202.  
  203.      $job->run(posix_getpid()); 
  204.  
  205.     } 
  206.  
  207.     exit(0);//子进程运行完成后退出 
  208.  
  209.     /***************子进程工作范围**********************/ 
  210.  
  211.    }else
  212.  
  213.     $this->workerPids[$pid] = $job
  214.  
  215.    } 
  216.  
  217.   } 
  218.  
  219.  } 
  220.  
  221.  function workerInstallSignal() 
  222.  
  223.  { 
  224.  
  225.   pcntl_signal(SIGUSR1,[__CLASS__,'workerHandleSignal'],false); 
  226.  
  227.  } 
  228.  
  229.  function workerHandleSignal($signal
  230.  
  231.  { 
  232.  
  233.   switch ($signal){ 
  234.  
  235.    case SIGUSR1: 
  236.  
  237.     $state = "worker pid=".posix_getpid()."接受了父进程发来的自定义信号\n"
  238.  
  239.     file_put_contents($this->state_file,$state,FILE_APPEND); 
  240.  
  241.     break
  242.  
  243.   } 
  244.  
  245.  } 
  246.  
  247.  function installSignal() 
  248.  
  249.  { 
  250.  
  251.   pcntl_signal(SIGINT,[__CLASS__,'handleMasterSignal'],false); 
  252.  
  253.   pcntl_signal(SIGTERM,[__CLASS__,'handleMasterSignal'],false); 
  254.  
  255.   pcntl_signal(SIGUSR1,[__CLASS__,'handleMasterSignal'],false); 
  256.  
  257.  } 
  258.  
  259.  function handleMasterSignal($signal
  260.  
  261.  { 
  262.  
  263.   switch ($signal){ 
  264.  
  265.    case SIGINT: 
  266.  
  267.     //主进程接受到中断信号ctrl+c 
  268.  
  269.     foreach ($this->workerPids as $pid=>$worker){ 
  270.  
  271.      posix_kill($pid,SIGINT);//向所有的子进程发出 
  272.  
  273.     } 
  274.  
  275.     exit("服务平滑停止\n"); 
  276.  
  277.     break
  278.  
  279.    case SIGTERM://ctrl+z 
  280.  
  281.     foreach ($this->workerPids as $pid=>$worker){ 
  282.  
  283.      posix_kill($pid,SIGKILL);//向所有的子进程发出 
  284.  
  285.     } 
  286.  
  287.     exit("服务停止\n"); 
  288.  
  289.     break
  290.  
  291.    case SIGUSR1://用户自定义信号 
  292.  
  293.     if (file_exists($this->state_file)){ 
  294.  
  295.      unlink($this->state_file); 
  296.  
  297.     } 
  298.  
  299.     foreach ($this->workerPids as $pid=>$worker){ 
  300.  
  301.      posix_kill($pid,SIGUSR1); 
  302.  
  303.     } 
  304.  
  305.     $state = "master pid\n".$this->getMasterPid()."\n"
  306.  
  307.     while(!file_exists($this->state_file)){ 
  308.  
  309.      sleep(1); 
  310.  
  311.     } 
  312.  
  313.     $state.= file_get_contents($this->state_file); 
  314.  
  315.     echo $state.PHP_EOL; 
  316.  
  317.     break
  318.  
  319.   } 
  320.  
  321.  } 
  322.  
  323. }  
  324.  
  325.  
  326. /**\ 
  327.  
  328.  * Created by PhpStorm.\ * User: 1655664358@qq.com 
  329.  
  330.  * Date: 2018/3/26\ * Time: 14:37\ */\namespace Chen\Worker; 
  331.  
  332. class Job 
  333.  
  334.  
  335.  public $job_run_time = 3600; 
  336.  
  337.  function run($pid
  338.  
  339.  {\sleep(3); 
  340.  
  341.  echo "worker pid = $pid job 没事干,就在这里job\n"
  342.  
  343.  } 
  344.  
  345. }  
  346.  
  347.  
  348. /** 
  349.  
  350.  * Created by PhpStorm.\ * User: 1655664358@qq.com 
  351.  
  352.  * Date: 2018/3/26\ * Time: 14:37\ */\namespace Chen\Worker; 
  353.  
  354. class Talk 
  355.  
  356.  
  357.  public $job_run_time = 3600; 
  358.  
  359.  function run($pid
  360.  
  361.  {\sleep(3); 
  362.  
  363.  echo "worker pid = $pid job 没事干,就在这里talk\n"
  364.  
  365.  } 
  366.  
  367.  
  368.  
  369. /** 
  370.  
  371.  * Created by PhpStorm.\ * User: 1655664358@qq.com 
  372.  
  373.  * Date: 2018/3/26\ * Time: 15:45\ */ 
  374.  
  375. require_once 'vendor/autoload.php'
  376.  
  377. $process = new \Chen\Worker\Server(); 
  378.  
  379. $process->workerJob = [new \Chen\Worker\Talk(),new \Chen\Worker\Job()]; 
  380.  
  381. $process->run();

PHP多进程 PHP信号中断 PHP内存

Tags: PHP多进程 PHP信号中断 PHP内存

分享到: