用户
 找回密码
 入住 CI 中国社区
搜索
查看: 3411|回复: 3
收起左侧

关于守护进程的几个百撕不得骑姐的问题[希望管理员顶起...

[复制链接]
发表于 2013-7-11 16:48:51 | 显示全部楼层 |阅读模式
先说一下我的需求:服务器产生一个常驻进程,此进程用来运行我们的php脚本,在脚本中我们会用到pcntl系列方法,而且我们希望每次fork N个子进程,多余的子进程放进一个队列里(数组队列),当N个子进程运行结束的时候在运行队列中的N个子进程,以此类推,直到全部进程结束.
下面是我们官方手册上找的一个NB的列子,顺便写上我做的注释(可能翻译的很狗血),我先把自己不懂得提出来
1:脚本开始就用declare(ticks=1); 目的何为,(google了一下,发现说的都很模糊,英文又看不懂,我哭了...),注释了后程序一直处于等待状态算是一个死循环吧.
2:出现的两个sleep(),其中一个或者全部注释后程序运行和情况一相同,为什么sleep后(或者sleep时)程序貌似进入另一种循环状态(这点我说不清楚,因为我看不出来他是怎么调用launchJob方法或者是run方法)
3:这个程序没有执行childSignalHandler这个方法,那他是如何解决N个子进程结束后在执行另外N个子进程
4:请大牛把这个例子的执行流程清晰的写下来(万分感谢啊{:soso_e105:}),并把关键点和需要额外补充的知识点说一下(还是万分感谢啊{:soso_e105:})

PHP复制代码
 
<?php
/**
 * Using pcntl_fork() can be a little tricky in some situations.  For fast jobs, a child can finish processing before the parent process has executed some code related to
 *  the launching of the process.  The parent can receive a signal before it's ready to handle the child process' status.  To handle this scenario, I add an id to a
 * "queue" of processes in the signal handler that need to be cleaned up if the parent process is not yet ready to handle them.  
 * I am including a stripped down version of a job daemon that should get a person on the right track.
 *
 * @author Sammy Guergachi <sguergachi at gmail.com>
 */

 
 
 
declare(ticks=1);
//A very basic job daemon that you can extend to your needs.
class JobDaemon{
 
    public $maxProcesses = 2;
    protected $jobsStarted = 0;
    protected $currentJobs = array();
    protected $signalQueue=array();  
    protected $parentPID;
 
    public function __construct(){
        echo "constructed \n";
        $this->parentPID = getmypid();
        pcntl_signal(SIGCHLD, array($this, "childSignalHandler"));
    }
 
    /**
    * Run the Daemon
    * 运行守护进程
    */

    public function run(){
        echo "Running \n";
        for($i=0; $i<10; $i++){
            $jobID = rand(0,10000000000000);
 
            while(count($this->currentJobs) >= $this->maxProcesses){
               echo "Maximum children allowed, waiting...\n";
               sleep(1);
            }
 
            $launched = $this->launchJob($jobID);
        }
 
        //Wait for child processes to finish before exiting here
        //等待子进程完成之前离开这里
        while(count($this->currentJobs)){
            echo "Waiting for current jobs to finish... \n";
            sleep(1);
        }
    }
 
    /**
    * Launch a job from the job queue
    * 从作业(任务)队列启动工作
    */

    protected function launchJob($jobID){
        //创建子进程
        $pid = pcntl_fork();
        if($pid == -1){
            //Problem launching the job
            //启动作业出问题了
            error_log('Could not launch new job, exiting');
            return false;
        }else if ($pid){
            // Parent process
            // Sometimes you can receive a signal to the childSignalHandler function before this code executes if
            // the child script executes quickly enough!
            // 父进程
            //如果子脚本执行不够快.在代码执行前,有时候你会从childSignalHandler函数中接收一个信号
            $this->currentJobs[$pid] = $jobID;
 
            // In the event that a signal for this pid was caught before we get here, it will be in our signalQueue array
            // So let's go ahead and process it now as if we'd just received the signal
            //在这个pid的信号被捕捉之前我们到达,它将在我们signalQueue数组中
            //因此让我们继续前进并处理他,如果我们刚刚收到信号
            if(isset($this->signalQueue[$pid])){
                echo "found $pid in the signal queue, processing it now \n";
                $this->childSignalHandler(SIGCHLD, $pid, $this->signalQueue[$pid]);
                unset($this->signalQueue[$pid]);
            }
        }else{
            //Forked child, do your deeds....
            //forked的子进程,做你想做的
            $exitStatus = 0; //Error code if you need to or whatever 如果你需要或任何的错误代码
            echo "Doing something fun in pid ".getmypid()."\n";
            exit($exitStatus);
        }
        return true;
    }
 
    public function childSignalHandler($signo, $pid=null, $status=null){
 
        //If no pid is provided, that means we're getting the signal from the system.  Let's figure out
        //which child process ended
        //如果没有pid被提供,这意味着我们从系统得到信号,让我们弄清楚哪个子进程结束了
        if(!$pid){
            //等待或返回fork的子进程状态
            $pid = pcntl_waitpid(-1, $status, WNOHANG);
        }
 
        //Make sure we get all of the exited children
        //确保我们得到所有已退出的子进程
        while($pid > 0){
            if($pid && isset($this->currentJobs[$pid])){
                //返回一个中断的子进程的返回代码
                $exitCode = pcntl_wexitstatus($status);
                if($exitCode != 0){
                    echo "$pid exited with status ".$exitCode."\n";
                }
                unset($this->currentJobs[$pid]);
            }
            else if($pid){
                //Oh no, our job has finished before this parent process could even note that it had been launched!
                //Let's make note of it and handle it when the parent process is ready for it
                //我们的工作已完成,在此之前的父进程甚至可能会注意到,它已启动!
                //注意,当父进程准备好时处理它
                echo "..... Adding $pid to the signal queue ..... \n";
                $this->signalQueue[$pid] = $status;
            }
            $pid = pcntl_waitpid(-1, $status, WNOHANG);
        }
        return true;
    }
}
$daemon = new JobDaemon();
$daemon -> run();
?>
 
复制代码

原例子:http://www.php.net/manual/en/function.pcntl-fork.php#98711
 楼主| 发表于 2013-7-11 16:49:26 | 显示全部楼层
我先顶起一下,大家把此贴顶起来啊,让更多的人看到.
 楼主| 发表于 2013-7-11 20:07:05 | 显示全部楼层
为什么没有人回答呢,难道我的问题很幼稚吗,跪求高人指点啊
发表于 2015-1-6 12:58:23 | 显示全部楼层
存数据库就简单多了,加个执行完的时间就行了,

本版积分规则