php守护进程函数 后台执行脚本的实例详解

发布时间:2020-02-06编辑:脚本学堂
本文介绍下,php中的守护进程函数,以及在后台执行脚本的例子,有需要的朋友,参考下吧。

我们经常通过crontab定时执行后端脚本。比如每10秒检查一下用户状态。
例子:
 

复制代码 代码示例:
@file: /php_scripts/scan_userstatus.php
#!/usr/bin/env php -q  <?php  $status = has_goaway();  if ($status) {      //done  }  ?>

通过crontab定时执行脚本scan_userstatus.php
 

复制代码 代码示例:
#echo “*:*/10 * * * * /php_scripts/scan_userstatus.php”

这样,每隔10秒钟,就会执行该脚本。

在短时间内,该脚本的内存资源还没有释放完,又启用了新的脚本。
即,新脚本启动了,旧脚本占用的资源还没有如愿释放。
如此,日积月累,浪费了很多内存资源。

对这个脚本进行如下的改进:
 

复制代码 代码示例:
@file: /php_scripts/scan_userstatus.php
#/usr/bin/env php -q  <?php  while (1) {      $status = has_goaway();      if ($status) {          //done      }      usleep(10000000);  }  ?>

这样,不需要crontab了。可以通过以下命令执行脚本,达到相同的功能效果
 

复制代码 代码示例:
#chmod +x /php_scripts/scan_userstatus.php
#nohup /php_scripts/scan_userstatus.php &
 

在这里,通过&将脚本放到后台运行,为了防止随着终端会话窗口关闭进程被杀,使用了nohup命令。
有办法不使nohup命令,也能够运行吗,就像Unin/linux Daemon一样。

这里就要讲到守护进程函数了。

什么是守护进程?一个守护进程通常补认为是一个不对终端进行控制的后台任务。
它有三个很显著的特征:在后台运行,与启动他的进程脱离,无须控制终端。
常用的实现方式是fork() -> setsid() -> fork() 详细如下:
 

复制代码 代码示例:

@file: /php_scripts/scan_userstatus.php

#/usr/bin/env php -q  <?php  daemonize();  while (1) {      $status = has_goaway();      if ($status) {          //done      }      usleep(10000000);  }     function daemonize() {      $pid = pcntl_fork();      if ($pid === -1 ) {          return FALSE;      } else if ($pid) {          usleep(500);          exit();                //exit parent      }         chdir("/");      umask(0);      $sid = posix_setsid();      if (!$sid) {          return FALSE;      }         $pid = pcntl_fork();      if ($pid === -1) {          return FALSE;      } else if ($pid) {          usleep(500);          exit(0);      }         if (defined('STDIN')) {          fclose(STDIN);      }      if (defined('STDOUT')){          fclose(STDOUT);      }      if (defined('STDERR')) {          fclose(STDERR);      }  }  ?>

实现了守护进程函数以后,则可以建立一个常驻进程,所以只需要执行一次:
 

复制代码 代码示例:
#/php_scripts/scan_userstatus.php

关键的二个php函数是pcntl_fork()和posix_setsid()。

fork()一个进程,则表示创建了一个运行进程的副本,副本被认为是子进程,而原始进程被认为是父进程。当fork()运行之后,则可以脱离启动他的进程与终端控制等,也意味着父进程可以自由退出。

pcntl_fork()返回值,-1表示执行失败,0表示在子进程中,而返进程ID号,则表示在父进程中。在这里,退出父进程。setsid(),它首先使新进程成为一个新会话的“领导者”,最后使该进程不再控制终端,这也是成为守护进程最关键的一步,这意味着,不会随着终端关闭而强制退出进程。对于一个不会被中断的常驻进程来说,这是很关键的一步。进行最后一次fork(),这一步不是必须的,但通常都这么做,它最大的意义是防止获得控制终端。(在直接打开一个终端设备,而且没有使用O_NOCTTY标志的情况下, 会获得控制终端).

总结:
1) chdir() 将守护进程放到总是存在的目录中,另外一个好处是,你的常驻进程不会限制你umount一个文件系统。

2)umask() 设置文件模式,创建掩码到最大的允许限度。
如果一个守护进程需要创建具有可读,可写权限的文件,一个被继承的具有更严格权限的掩码会有反作用。

3)fclose(STDIN), fclose(STDOUT), fclose(STDERR) 关闭标准I/O流。注意,如果有输出(echo),则守护进程会失败。
通常将STDIN, STDOUT, STDERR重定向某个指定文件.