pcntl_wait调用问题

back0893

下面是我测试的代码,我发现如果在子进程中给父进程发送一个信号,如果使用pcntl_wait将会等待子进程退出才能执行
因为我使用php7.2测试的,所以是php版本导致?
我也看了网站里面的其他提问,冒失父进程都会阻塞到子进程退出

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

$master_id = getmypid();

$pid = pcntl_fork();
if($pid > 0)
{
    pcntl_signal(SIGINT,'stopAll') ;
   pcntl_signal_dispatch();
    $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";
    echo "send signal to master\n";
    posix_kill($master_id, SIGINT);
    sleep(60);
}
4321 3 0
3个回答

back0893

因为我再看源码,在自己实现给父进程发送信号时方向,信号会被阻塞到子进程退出才会执行,,,,,
em,有点不明白stop是咋个实现的,因为我看源码也是给父进程发送一个信号
还是因为我是用cli没有用守护进程问题?

  • 暂无评论
back0893

还是手册没看明白
我知道了 pcntl_signal(SIGINT,'stopAll')
这里需要 添加第三个参数 默认是true,这样子进程的sleep就不会执行了 pcntl_signal(SIGINT,'stopAll',false) 父进程信号就会被立即执行,并且pcntl_wait返回的值是-1

  • 暂无评论
blogdaren

你虽然找到了关键的第三个参数,但是原理部分我认为理解错了呢,而且示例代码也是个问题代码:
1、你说的 "添加第三个参数 默认是true,这样子进程的sleep就不会执行了", 并不是这样的,子进程并未退出,而是休眠了,如果你写的不是sleep,而是死循环,那就成了典型的僵尸进程了【ps aux 或 strace下就会看到真相】
2、信号是可以中断 wait 等系统调用的
3、pcntl_signal(SIGINT,'stopAll',false) 函数的第三个参数是代表进程在收到信号后是内核是否重启系统调用。
4、根据上述条款2理论,示例代码中设置为false,那么主进程中 pcntl_wait立即被中断并返回了 -1,这个-1是代表异常出错了,而出错的原因是因为子进程并没有正常退出呢。
5、官方的 stopAll() 实现原理也完全不是你示例代码所写的那样的,大致原理是:
 (1) 父子进程均要安装信号处理器
 (2) 父进程负责收集子进程并监控起来等待子进程退出
 (3) 父进程会收到某种信号,之后将信号在发送给所有的子进程
 (4) 子进程收到信号后执行相应的逻辑处理并退出
 (5) 父进程若在约定期限依然尚未退出,则SIGKILL强行干掉

  • back0893 2018-11-25

    1.这里就是我疑惑的,我使用posix_kill 在子进程中发送给父进程,父进程注册的信号处理器,被我手动调用,应该执行父进程的,但是父进程被阻塞直到子进程60s后退出才会触发信号
    2.pcntl_async_signals(true) 来自动调用也是一样的,会被阻塞
    3.父进程监控子进程是否退出,子进程是一个死循环,这里会父进程会被阻塞到所有的子进程退出,父进程收到了信号才会被执行

  • back0893 2018-11-25

    应该就是这样的,可能我理解不对,linux调用不明白,因为我看worker实现也是这样的.其实我是fork你的网站上分享的那个demo

  • blogdaren 2018-11-25

    @5032:
    1、我建议你把我列的那几点再反复的看几遍呢,深入理解。
    2、父进程并不是因子进程sleep了60后才触发父进程的执行,换句话,父进程阻塞是因为调用了wait 系统调用。
    3、子进程一开始执行就立即发送了信号给父进程,父进程因dispatch也会随即检测到信号,上面说了并不是子进程休眠导致的,关键地方就在这里: 是因为一开始你设置的true参数,这个会通知内核立即重启系统调用,从而再次发生阻塞,当你设置为false后,父进程立刻执行,子进程或休眠或变为僵尸进程,除了strace看到真相外,这也是最好的证明了。

  • blogdaren 2018-11-25

    @5032: 我网站的那段代码本身没有问题,那仅用作调试信号工作原理,我是根据你题示说的是 stopAll() 的实现,那么那段demo代码肯定是不能那么写的。

  • back0893 2018-11-26

    @614:嗯嗯,我在自己磋磨下

  • back0893 2018-11-26

    @614:可以这样理解不,虽然信号发送成功,如果内核立即重启系统调用那么主进程的信号处理人被阻塞?

  • blogdaren 2018-11-26

    1、信号处理被阻塞? 这是什么概念哦,没有这说法,这里的阻塞指的是进程阻塞,进程阻塞是因为进行了比如wait系统调用。
    2、内核立即重启系统调用必然会再次发生阻塞,除非再次检测到新来信号,因为收到一次信号只会中断一次系统调用。

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