PHP7实现daemon守护进程详解-php教程

资源魔 42 0
本篇文章次要讲述的是用PHP7完成daemon守护过程,具备肯定的参考代价,感兴味的冤家能够理解一下。

正在一个多义务的较量争论机操作零碎中,守护过程是一种正在后盾执行的较量争论机顺序。此类顺序会被以过程的方式初始化。守护过程顺序的称号通常以字母“d”末端:例如,syslogd就是指治理零碎日记的守护过程。

daemon 顺序是不断运转的效劳端顺序,又称为守护过程。通常正在零碎后盾运转,不管制终端没有与前台交互,daemon 顺序普通作为零碎效劳应用。daemon 是长期运转的过程,通常正在零碎启动后就运转,正在零碎封闭时才完结。普通说Daemon顺序正在后盾运转,是由于它不管制终端,无奈以及前台的用户交互。daemon顺序普通都作为效劳顺序应用,期待客户端顺序与它通讯。咱们也把运转的daemon顺序称作守护过程。

通常,守护过程不任何存正在的父过程(即PPID=1),且正在UNIX零碎过程层级中间接位于init之下。守护过程顺序通常经过以下办法使本人成为守护过程:对一个子过程运转fork,而后使其父过程立刻终止,使患上这个子过程能正在init下运转。这类办法通常被称为“脱壳”。

零碎通常正在启动时一起起动守护过程。守护过程为对网络申请,硬件流动等进行呼应,或其余经过某些义务对其余使用顺序的申请进行回应提供支持。守护过程也可以对硬件进行设置装备摆设(如正在某些Linux零碎上的devfsd),运转方案义务(例如cron),和运转其余义务。每一个过程都有一个父过程,子过程加入,父过程能失去子过程加入的状态。

守护过程简略地说就是能够脱离终端而正在后盾运转的过程 . 这正在Linux中长短经常见的一种过程 , 比方apache或许mysql等效劳启动后 , 就会以守护过程的形式进驻正在内存中 。守护顺序是正在后盾运转的使用顺序,而没有是由用户间接操作。守护过程的例子是Cron以及MySQL。 应用PHP守护过程十分简略,而且需求应用PHP 4.1或更高版本编译参数:--enable-pcntl

如果有个耗工夫的义务需求跑正在后盾 : 将一切mysql中user表中的2000万用户全副导入到redis中做预热缓存 , 那末这个义务预计一时半会是没有会完结的 , 这个时分就需求编写一个php剧本以daemon方式运转正在零碎中 , 完结后主动推出。

正在Linux中 , 有三种形式完成剧本后盾化 :

1 . 正在饬令后增加一个&符号

比方 php task.php & . 这个办法的缺陷正在于 假如terminal终端封闭 , 无论是失常封闭仍是非失常封闭 , 这个php过程城市跟着终端封闭而封闭 , 其次是代码中假如有echo或许print_r之类的输入文本 , 会被输入到以后的终端窗口中 。

2 . 应用nohup饬令

比方 nohup php task.php & . 默许状况下 , 代码中echo或许print_r之类输入的文本会被输入到php代码同级目次的nohup.out文件中 . 假如你用exit饬令或许封闭按钮等失常手法封闭终端 , 该过程没有会被封闭 , 仍然会正在后盾继续运转 . 然而假如终端遇到异样加入或许终止 , 该php过程也会随即加入 . 实质上 , 也并不是稳固牢靠的daemon计划 。

3 . 经过 pcntlposix 扩大完成

编程中需求留意之处有:

  • 经过二次 pcntl_fork()posix_setsid 让主过程脱离终端
  • 经过 pcntl_signal() 疏忽或许解决 SIGHUP 旌旗灯号
  • 多过程顺序需求经过二次 pcntl_fork() 或许 pcntl_signal() 疏忽 SIGCHLD 旌旗灯号避免子过程变为 Zombie 过程
  • 经过 umask() 设定文件权限掩码,避免承继文件权限而来的权限影响性能
  • 将运转过程的 STDIN/STDOUT/STDERR 重定向到 /dev/null 或许其余流上

daemon有以下特色:

  • 不终端
  • 后盾运转
  • 父过程 pid 为1

想要查看运转中的守护过程能够经过 ps -ax 或许 ps -ef 查看,此中 -x 示意会列出不管制终真个过程。

fork 零碎挪用

fork 零碎挪用用于复制一个与父过程简直齐全相反的过程,重生成的子过程没有同之处正在于与父过程有着没有同的 pid 和有没有同的内存空间,依据代码逻辑完成,父子过程能够实现同样的工作,也能够没有同。子过程会从父过程中承继比方文件形容符一类的资本。

PHP 中的 pcntl 扩大中完成了 pcntl_fork() 函数,用于正在 PHP 中 fork 新的过程。

setsid 零碎挪用

setsid 零碎挪用则用于创立一个新的会话并设定过程组 id。这里有几个概念:会话过程组

  正在 Linux 中,用户登录孕育发生一个会话(Session),一个会话中蕴含一个或许多个过程组,一个过程组又蕴含多个过程。每一个过程组有一个组长(Session Leader),它的 pid 就是过程组的组 id。过程组长一旦关上一个终端,这一个终端就被称为管制终端。一旦管制终端发作异样(断开、硬件谬误等),会收回旌旗灯号到过程组组长。

  后盾运转顺序(如 shell 中以&末端执行指令)正在终端封闭之后也会被杀死,就是不解决好管制终端断开时收回的SIGHUP旌旗灯号,而SIGHUP旌旗灯号关于过程的默许行为则是加入过程。

挪用 setsid 零碎挪用之后,会让以后的过程新建一个过程组,假如正在以后过程中没有关上终真个话,那末这一个过程组就没有会存正在管制终端,也就没有会呈现由于封闭终端而杀死过程的成绩。

PHP 中的 posix 扩大中完成了 posix_setsid() 函数,用于正在 PHP 中设定新的过程组。

二次 fork 的作用

起首,setsid 零碎挪用不克不及由过程组组长挪用,会前往-1。

二次 fork 操作的样例代码以下:

$pid1 = pcntl_fork();

if ($pid1 > 0) {
// 父过程会失去子过程号,以是这里是父过程执行的逻辑 exit('parent process. 1'."\n"); } else if ($pid1 < 0) { exit("Failed to fork 1\n"); } if (-1 == posix_setsid()) { exit("Failed to setsid\n"); } $pid2 = pcntl_fork(); if ($pid2 > 0) { exit('parent process. 2'."\n"); } else if ($pid2 < 0) { exit("Failed to fork 2\n"); }

pcntl_fork() 函数创立一个子过程,这个子过程仅PID(过程号) 以及PPID(父过程号)与其父过程没有同。

前往值

  胜利时,正在父过程执行线程内前往孕育发生的子过程的PID,正在子过程执行线程内前往 0,失败时,正在 父过程上下文前往 -1,没有会创立子过程,而且会诱发一个PHP谬误。

假设咱们正在终端中执行使用顺序,过程为 a,第一次 fork 会天生子过程 b,假如 fork 胜利,父过程 a 加入。b 作为孤儿过程,被 init 过程托管。

此时,过程 b 处于过程组 a 中,过程 b 挪用 posix_setsid 要求天生新的过程组,挪用胜利后以后过程组变成 b。


php fork2.php 
parent process. 1
parent process. 2

此时过程 b 现实上曾经脱离职何的管制终端,例程:


cli_set_process_title('process_a');

$pidA = pcntl_fork();

if ($pidA > 0) {
    exit(0);
} else if ($pidA < 0) {
    exit(1);
}

cli_set_process_title('process_b');

if (-1 === posix_setsid()) {
    exit(2);
}

while(true) {
    sleep(1);
}

执行顺序之后:  


$ php cli-title.php 
$ ps ax | grep -v grep | grep -E 'process_|PID'
  PID TTY      STAT   TIME COMMAND
15725 ?        Ss     0:00 process_b

从新关上一个shell窗口,成果同样,都正在呢

从 ps 的后果来看,process_b 的 TTY 曾经变为了 ,即不对应的管制终端。

代码走到这里,仿佛曾经实现了性能,封闭终端之后 process_b 也不被杀死,然而为何还要进行第二次 fork 操作呢?

StackOverflow 上的一个答复写的很好:

The second fork(2) is there to ensure that the new process is not a session leader, so it won’t be able to (accidentally) allocate a controlling terminal, since daemons are not supposed to ever have a controlling terminal.

这是为了避免实际的工作的过程自动联系关系或许不测联系关系管制终端,再次 fork 之后天生的新过程因为没有是过程组组长,是不克不及请求联系关系管制终真个。

综上,二次 fork 与 setsid 的作用是天生新的过程组,避免工作过程联系关系管制终端。 

写一个demo测试下


<?php
// 第一次fork零碎挪用
$pid_A = pcntl_fork();

// 父过程 以及 子过程 城市执行上面代码
if ($pid_A < 0) {
    // 谬误解决: 创立子过程失败时前往-1.
    exit('A fork error ');
} else if ($pid_A > 0) {
     // 父过程会失去子过程号,以是这里是父过程执行的逻辑
    exit("A parent process exit \n");
}

// B 作为孤儿过程,被 init 过程托管,此时,过程 B 处于过程组 A 中

// 子过程失去的$pid为0, 以是如下是子过程执行的逻辑,受管制终真个影响,管制终端封闭则这里也会加入

// [子过程] 管制终端未封闭前,将以后子过程晋升会会话组组长,及过程组的leader
// 过程 B 挪用 posix_setsid 要求天生新的过程组,挪用胜利后以后过程组变成 B
if (-1 == posix_setsid()) {
    exit("Failed to setsid\n");
}

// 此时过程 B 曾经脱离职何的管制终端

// [子过程]  这时候候正在【过程组B】中,从新fork零碎挪用(二次fork)
$pid_B = pcntl_fork();
if ($pid_B < 0) {
    exit('B fork error ');
} else if ($pid_B > 0) {
    exit("B parent process exit \n");
}

// [新子过程] 这里是重生成的过程组,没有受管制终真个影响,写写本人的营业逻辑代码
for ($i = 1; $i <= 100; $i++) {
    sleep(1);
    file_put_contents('daemon.log',$i . "--" . date("Y-m-d H:i:s", time()) . "\n",FILE_APPEND);
}

Window 下跑回间接抛出异样


php runtime\daemon.php
PHP Fatal error:  Uncaught Error: Call to undefined function pcntl_fork() in D:\phpStudy\PHPTutorial\WWW\notes\runtime\daemon.php:13
Stack trace:
#0 {main}
  thrown in D:\phpStudy\PHPTutorial\WWW\notes\runtime\daemon.php on line 13

Linux 下执行,输入后果


php daemon.php
... 97--2018-09-07 03:50:09 98--2018-09-07 03:50:10 99--2018-09-07 03:50:11 100--2018-09-07 03:50:12

以是,如今即便封闭了终端,改剧本任然正在后盾守护过程运转

相干教程:PHP视频教程

以上就是PHP7完成daemon守护过程详解的具体内容,更多请存眷资源魔其它相干文章!

标签: php开发教程 php开发资料 php开发自学 PHP7 daemon守护进程

抱歉,评论功能暂时关闭!