关于pcntl_wait与信号中断触发的问题

颗粒Cc.

请教一下,在workerman发送信号SIGINT给主进程,主进程(pcntl_wait)会立刻收到信号并执行信号处理函数,
我模拟了下,主进程(pcntl_wait)会一直阻塞,并不会立刻执行信号处理函数,信号已经注册入队列当中了。假设我发送了好几次信号,最终如果按control+c中断的时候,就会输出这几次信号的处理函数。

首先感谢BOSS:),希望知道workerman是如何处理的,我看了好多次,还是找不出所以然-。-,太笨了。

代码如下

    function stopAll($sig){
    echo "master has a sig $sig\n" ;
    global $pid ;
    echo $pid."\n" ;
    if($pid > 0) {
        posix_kill($pid,$sig) ;
    }
}

$pid = pcntl_fork();
if($pid > 0)
{
    pcntl_signal(SIGINT,'stopAll') ;
    $epid = pcntl_wait($status,WUNTRACED);
    $id = getmypid();
    echo "parent process {$id}, child process {$pid}\n";
    if($epid){
        echo "child $epid exit \n" ;
    }
    pcntl_signal_dispatch();
}
else
{
       $id = getmypid();
    echo "child process,pid {$id}\n";
    while(1){
        sleep(2) ;
    }
}

我发送了好几次信号,仍然阻塞,最后以control+c中断输出如下:

qmoredeMacBook-Pro:120.25.105.202 Qmore$ php recive_sig.php 
child process,pid 24793
^[[A^Cmaster has a sig 2
24793
master has a sig 2
24793
master has a sig 2
24793
master has a sig 2
24793
master has a sig 2
24793
parent process 24792, child process 24793
child 24793 exit 
6929 9 0
9个回答

walkor
else
{
    $id = getmypid();
    echo "child process,pid {$id}\n";
    while(1){
        sleep(2) ;
        pcntl_signal_dispatch();
    }
}

需要在子进程部分及时检查信号并处理,加一句pcntl_signal_dispatch就好了。
信号回调不会自己自动执行的,要么有声明declare(ticks=1)要么调用主动pcntl_signal_dispatch检查信号。

颗粒Cc.

因为主进程阻塞在pcntl_wait,除非是子进程退出,那么主进程才会继续下一步,才有可能主动pcntl_signal_dispatch检查信号。如果发送信号给主进程,主进程还是阻塞在pcntl_wait呢,没法触发pcntl_signal_dispatch.>.<

  • 暂无评论
walkor

pcntl_wait其实就是wait系统调用,是可以被信号打断的,当信号到来后pcntl_wait会立刻返回。
同理sleep也是系统调用,也可以被信号打断停止睡眠立刻返回。
所以在pcntl_wait或者sleep下的pcntl_signal_dispatch函数在收到信号后会立刻被执行。

  • hk2018 2019-12-14

    echo posix_getpid().PHP_EOL;
    pcntl_async_signals(true);

    pcntl_signal(SIGINT, function(){
    echo posix_getpid()." get signal ".date("H:i:s")."\n";

    });

    $pid = pcntl_fork();

    if ($pid) {
    E:
    pcntl_wait($status, WUNTRACED);
    echo pcntl_strerror(pcntl_get_last_error())." pcntl_wait return\n";
    //被信号打断
    if(pcntl_get_last_error()==4){
    goto E;
    }
    } else {
    echo "son:".posix_getpid().PHP_EOL;
    $i=0;
    while(1){
    echo date("H:i:s").PHP_EOL;
    sleep(1);
    $i++;
    if($i>5){
    exit(100);
    }
    }
    }

    这段代码在pcntl_signal 不传第3个参数时,子进程的sleep会被信号打断,父进程的wait为啥不会被打断呢?只是把信号加入队列

  • hk2018 2019-12-14

    php是7.3版本的

颗粒Cc.

恩恩,我就是这点疑惑,我利用另外的一个程序,像主进程发送了SIGINT信号,但是他并不会被打断,只会将信号加入信号队列。

  • 暂无评论
颗粒Cc.

我再去理解理解,多谢boss

  • 暂无评论
walkor

不客气

  • 暂无评论
soooldier

看了下两位的回复,感觉其中是有问题的。楼主的问题是“在workerman发送信号SIGINT给主进程,主进程(pcntl_wait)会立刻收到信号并执行信号处理函数”,而在示例代码中的逻辑比较混乱。在walkor给出的答案里说到了子进程添加pcntl_signal_dispatch(),但是注册信号回调的代码缺是在主进程。最后贴一下描述“在workerman发送信号SIGINT给主进程,主进程(pcntl_wait)会立刻收到信号并执行信号处理函数”的代码,如有不正确的地方请多多指教:

function stopAll($sig){
    echo "master has a sig $sig\n" ;
}

$master_id = getmypid();

$pid = pcntl_fork();
if($pid > 0)
{
    pcntl_signal(SIGINT,'stopAll') ;
    $epid = pcntl_wait($status,WUNTRACED);
    pcntl_signal_dispatch();
    echo "parent process {$master_id}, child process {$pid}\n";
    if($epid){
        echo "child $epid exit \n" ;
    }
}
else
{
    $id = getmypid();
    echo "child process,pid {$id}\n";
    sleep(6);
    echo "send signal to master\n";
    posix_kill($master_id, SIGINT);
}
  • sunshine 2016-11-08

    不错哦

  • sunshine 2016-11-08

    可是上面的例子中pcntl_signal_dispatch()注释掉的话,也是正常的。为什么要在这个位置添加呢?

  • 张若初 2020-10-28

    经测试,主进程是在子进程退出后收到的信号,该改动无效

walkor

最后贴一下描述“在workerman发送信号SIGINT给主进程,主进程(pcntl_wait)会立刻收到信号并执行信号处理函数”的代码,如有不正确的地方请多多指教:

对的

  • soooldier 2016-10-09

    请教一下walkor大侠,pcntl_wait第二个参数WUNTRACED具体表示什么意思呢?手册里的解释没太看懂

张若初

最终的解决办法是在pcntl_signal()的第三个参数,设置未false,也就是说,系统调用不自动重新启动,可以收到信号,这个不理解

  • blogdaren 2020-10-29

    设置为true的话,wait会再次被自动调用,导致进程再次阻塞;另外一次信号只能中断一次系统调用。

年代过于久远,无法发表回答
🔝