监控程序--PHP后台守护进程(二)

        守护进程(daemon)就是一直在后台运行的进程(daemon)。守护进程通常有以下几个特点:

  1. 后台运行。

  2. 没有控制终端,终端名设置为?号:也就意味着没有 stdin 0 、stdout 1、stderr 2。

  3. 父进程不是用户创建的进程,init进程或者systemd(pid=1)以及用户人为启动的用户层进程一般以pid=1的进程为父进程,而以kthreadd内核进程创建的守护进程以kthreadd为父进程。

  4. 守护进程一般是会话首进程、组长进程。

  5. 工作目录为 \ (根目录),主要是为了防止占用磁盘导致无法卸载磁盘。


创建守护进程:

    冷暖自知一抹茶ck


要创建一个守护进程,一般进行如下步骤

  1. 如果是单例守护进程,结合锁文件和kill函数检测是否有进程已经运行。

  2. umask取消进程本身的文件掩码设置,也就是设置Linux文件权限,一般设置为000,这是为了防止子进程创建一个不能访问的文件(没有正确分配权限)。此过程并非必须,如果守护进程不会创建文件,也可以不修改。

  3. fork出子进程,父进程退出。这样子进程一定不是组长进程(进程id不等于进程组id)。

  4. 子进程调用setsid新建会话(使子进程变为会话首进程、组长进程,并断开终端)。

  5. 如果是单例守护进程,将pid写入到记录锁文件,一般为/var/run/xxx.pid。

  6. 切换工作目录到根目录,这是为了防止占用磁盘造成磁盘不能卸载。所以也可以改到别的目录,只要保证目录所在磁盘不会中途卸载。

  7. 重定向输入输入错误文件句柄,将其指向/dev/null。

下面以PHP的 pcntl_*系列函数 实现方式为例来说明。如下:

pcntl_fork:在当前进程内创建一个子进程。成功时,在父进程执行线程内返回产生的子进程的PID,在子进程执行线程内返回0。
失败时,在 父进程上下文返回-1,不会创建子进程,并且会引发一个PHP错误。

posix_setuid:设置当前进程的操作用户

posix_setgid:设置当前进程的操作用户所属分组

getmypid:获取当前进程id

posix_kill:向指定进程发送进程信号

pcntl_signal:安装一个信号处理器

system:执行外部程序,并且显示输出


        1、启动守护进程

        冷暖自知一抹茶ck

       2、停止守护进程

        停止守护进程,只需读取守护进程的pid文件,然后调用PHP函数posix_kill($pid, 9),最后将该文件删除,即可。如下:

        冷暖自知一抹茶ck

        3、重启守护进程

        重启守护进程,就是给守护进程发送SIGHUP信号。发送SIGHUP信号,既可以通过Linux命令kil发送 kill -s SIGHUP 64881,也可以通过PHP库函数 posix_kill(posix_getpid(), SIGUSR1)实现。如下:

        冷暖自知一抹茶ck

        4、完整代码

        冷暖自知一抹茶ck



        根据守护进程的规则和特点通过代码来实现,守护进程最大的特点就是脱离了用户终端和会话,下面是实现的代码,关键地方进行了注释。

<?php
 
$pid = pcntl_fork();
 
if ($pid == -1)
{
    throw new Exception('fork子进程失败');
}
elseif ($pid > 0)
{
    //父进程退出,子进程变成孤儿进程被1号进程收养,进程脱离终端;
    //子进程不是进程组长,以便接下来顺利创建新会话
    exit(0);
}
 
// 最重要的一步,创建一个新的会话,脱离原来的控制终端
if (posix_setsid() == -1) {
    die("could not detach from terminal");
}
 
// 修改当前进程的工作目录,由于子进程会继承父进程的工作目录,修改工作目录以释放对父进程工作目录的占用。
chdir('/');
 
/*
 * 通过上一步,我们创建了一个新的会话组长,进程组长,且脱离了终端,但是会话组长可以申请重新打开一个终端,为了避免
 * 这种情况,我们再次创建一个子进程,并退出当前进程,这样运行的进程就不再是会话组长。
 */
$pid = pcntl_fork();
if ($pid == -1)
{
    throw new Exception('fork子进程失败');
}
elseif ($pid > 0)
{
    //  再一次退出父进程,子进程成为最终的守护进程
    exit(0);
}
 
// 由于守护进程用不到标准输入输出,关闭标准输入,输出,错误输出描述符
fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);
 
/*
 * 处理业务代码
 */
 
while(TRUE)
{
    file_put_contents('log.txt', time().PHP_EOL, FILE_APPEND);
    sleep(5);
}


参考:

        php写守护进程(Daemon)

        PHP守护进程实例

        PHP后台守护进程的实现方式(Linux环境)

        PHP多进程系列笔记


冷暖自知一抹茶ck
请先登录后发表评论
  • 最新评论
  • 总共0条评论