zoukankan      html  css  js  c++  java
  • PHP守护进程化

    什么是守护进程?

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

    在glibc里有一个函数daemon。调用此函数,就可使当前进程脱离终端变成一个守护进程,具体内容参见man daemon。PHP中暂时没有此函数,PHP程序实现守护进程化有2种方法:

    1.使用系统命令nohup

    nohup php myprog.php > log.txt &

    &,这样执行程序虽然也是转为后台运行,但实际上是依赖终端的,当用户退出终端时进程就会被杀掉。需要使用nohup来实现

    2.使用supervisor工具 (推荐此方案)

      http://www.cnblogs.com/loveyouyou616/p/7028257.html

    3.当然也可以用程序实现(不建议生产环境使用)

    C程序实现:

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <sys/types.h>
     5 #include <sys/stat.h>
     6 #include <fcntl.h>
     7 
     8 //实现守护进程步骤
     9 void crete_daemon(void)
    10 {
    11     pid_t pid = 0;
    12     pid = fork();
    13     if (pid<0)
    14     {
    15         perror("fork");
    16         exit(-1);
    17     }
    18 
    19     if (pid > 0)
    20     {
    21         //1.父进程直接退出
    22         exit(0);
    23     }
    24 
    25     //2.
    26     //执行到这里就是子进程
    27     //setsid 将当前进程设置为一个新的会话期session,目的就是
    28     //让当前进程脱离控制台,成为守护进程。
    29     pid = setsid();
    30     if (pid < 0)
    31     {
    32         perror("setsid");
    33         exit(-1);
    34     }
    35 
    36     //3.设置当前进程的工作目录为根目录,不依赖于其他
    37     chdir("/");
    38 
    39     //4.umask设置为0确保将来进程有最大的文件操作权限
    40     umask(0);
    41 
    42     //5.关闭文件描述符
    43     //先要获取当前系统中所允许打开的最大文件描述符数目
    44     int i = 0;
    45     int cnt = sysconf(_SC_OPEN_MAX);
    46     for (i=0;i<cnt;i++)
    47     {
    48         close(i);
    49     }
    50 
    51     //将0,1,2定位到 /dev/null
    52     open("/dev/null",O_RDWR);
    53     open("/dev/null",O_RDWR);
    54     open("/dev/null",O_RDWR);
    55 
    56 }
    57 
    58 int main(void)
    59 {
    60 
    61     crete_daemon();
    62 
    63     while(1)
    64     {
    65         printf("I am running.
    ");
    66         sleep(1);
    67     }
    68 
    69     return 0;
    70 }

    PHP脚本函数实现:

    /*
     *根据c语言的实现思路即可。
     * 因为需要关闭 标准io,所以这里使用redis方便测试。
     */
    <?php
    //php代码实现守护进程
    function daemon(){
        $pid = pcntl_fork();
        if($pid < 0){
            die("fork(1) failed!
    ");
        }elseif($pid > 0){
            //1.父进程直接退出
            exit;
        }
    
        //执行到这里就是子进程
        //2.建立一个有别于终端的新session以脱离终端
        $sid = posix_setsid();
        if (!$sid) {
            die("setsid failed!
    ");
        }
    
        //这一部不是必须的
        $pid = pcntl_fork();
        if($pid < 0){
            die("fork(1) failed!
    ");
        }elseif($pid > 0){
            exit; //父进程退出, 剩下子进程成为最终的独立进程
        }
    
    
        //3.设置当前进程的工作目录为根目录,不依赖于其他
        chdir("/");
        //4.umask设置为0确保将来进程有最大的文件操作权限
        umask(0);
    
    
        //5.关闭标准I/O流
        if (defined('STDIN'))
            fclose(STDIN);
        if (defined('STDOUT'))
            fclose(STDOUT);
        if (defined('STDERR'))
            fclose(STDERR);
    }
    
    daemon();
    
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);
    
    while (true) {
        //echo 1; 不要任何输出echo 因为标准输入流关闭了,会异常导致进程终止
        $redis->set("name", "lemon".mt_rand());
        sleep(3);
    }

    测试结果:

    守护进程:

    这里较为关键的二个php函数是pcntl_fork()和posix_setsid()

    • fork()一个进程,则表示创建了一个运行进程的副本,副本被认为是子进程,而原始进程被认为是父进程。当fork()运行之后,则可以脱离启动他的进程与终端控制等,也意味着父进程可以自由退出。
    • setsid(),它首先使新进程成为一个新会话的“领导者”,最后使该进程不再控制终端,这也是成为守护进程最关键的一步,这意味着,不会随着终端关闭而强制退出进程。对于一个不会被中断的常驻进程来说,这是很关键的一步。
    • 进行最后一次fork(),这一步不是必须的,但通常都这么做,它最大的意义是防止获得控制终端。(在直接打开一个终端设备,而且没有使用O_NOCTTY标志的情况下, 会获得控制终端)

    其它事项说明:

    • chdir() 守护进程默认继承了父进程的当前工作目录,当系统磁盘发生umount时将造成诸多的麻烦,通常将”/” 作为守护进程的当前工作目录,可以避免上述的问题
    • umask() 守护进程默认继承了父进程的文件权限掩码,这就给该子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置为0,可以大大增强该守护进程的灵活性
    • fclose(STDIN), fclose(STDOUT), fclose(STDERR) 关闭标准I/O流。用fork函数新建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。
  • 相关阅读:
    Python开发基础--- IO模型
    进程队列补充-创建进程队列的另一个类JoinableQueue
    Python开发基础--- Event对象、队列和多进程基础
    Python开发基础---多线程锁机制
    Python开发基础----多线程
    Python开发基础----socket套接字基础2
    Python开发基础----异常处理、socket套接字基础1
    Python开发基础----反射、面向对象进阶
    Python开发基础---多态与多态性、绑定方法和非绑定方法
    ubuntu添加新用户
  • 原文地址:https://www.cnblogs.com/loveyouyou616/p/7867132.html
Copyright © 2011-2022 走看看