旗下导航:搜·么
当前位置:网站首页 > PHP教程 > 正文

PHP7完成daemon保卫历程详解【php教程】

作者:搜搜PHP网发布时间:2019-11-26分类:PHP教程浏览:90


导读:本篇文章重要报告的是用PHP7完成daemon保卫历程,具有肯定的参考价值,感兴趣的朋侪可以相识一下。在一个多使命的计算机操纵体系中,保卫历程是一种在背景实行的计算机顺序。...
本篇文章重要报告的是用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保卫历程详解的细致内容,更多请关注ki4网别的相干文章!

标签:PHP7daemon守护进程