信号是进程间通信的方式之一。比如子进程执行完成,则会发送 SIGCHLD 给父进程,告诉父进程,我执行完啦。

在linxu系统中,我们可以执行下面的命令来看看系统有哪些进程间的信号:

kill -l
HUP INT QUIT ILL TRAP ABRT EMT FPE KILL BUS SEGV SYS PIPE ALRM TERM URG STOP TSTP CONT CHLD TTIN TTOU IO XCPU XFSZ VTALRM PROF WINCH INFO USR1 USR2

常见的信号解释可看这里:https://my.oschina.net/lyongde/blog/509617

来看下下面的代码:

<?php
pcntl_async_signals(true);

$pid = pcntl_fork();
if ($pid === -1) {
    exit('无法创建子进程');
} elseif ($pid > 0) {
    // 父进程
    $pid = getmypid();
    echo "我是父进程:{$pid}\n";
    // SIGINT = crtl+c
    pcntl_signal(SIGINT, function () {
        echo "收到ctrl+c信号啦\n";
    });
    sleep(60);
} else {
    // 这里是子进程
    $pid = getmypid();
    echo "我是子进程:{$pid}\n";
    pcntl_signal(SIGINT, function () {
        echo "子进程:收到ctrl+c信号啦\n";
    });
    sleep(60);
}

上述代码中,我们在父进程和子进程中分别调用了 pcntl_signal 函数。该函数第一个参数是指定需要注册的信号,第二个参数是收到该信号后,执行的函数,也就是一个闭包。

这里还需要注意的是,在文件的开始,我们调用了 pcntl_async_signals(true) 函数。这个函数是干什么呢?

这个是函数其实是告诉系统异步的检测信号。上面我们调用 pcntl_signal 函数来注册信号的处理,但是它仅仅是注册,它并不负责在信号传递过来的时候执行这个注册的函数,所以想要等到信号来的时候执行这个注册的函数,则需要 pcntl_async_signals(true) 这个函数的支持。当然,这个特性只有 php>=7.1才有。低于该版本,可以这样:

<?php
declare(ticks = 1);

具体可参考:[https://www.php.net/manual/zh/function.pcntl-signal.php]

我们来执行下上面的代码:

+ php process4.php
我是父进程:17670
我是子进程:17671
^C收到ctrl+c信号啦
子进程:收到ctrl+c信号啦

如果程序默认没有注册 SIGINT 信号的话,那么当收到 SIGINT 信号的时候,系统将会自动将该程序终结掉。如果有注册的话,则交给程序本身注册的逻辑处理。

在执行的过程中,我们按下了 crtl+c 组合键,该组合键其实就是给当前程序发送 SIGINT 信号。

这里,当我们按下 ctrl+c 后,程序输出一些信息,表示父进程和子进程都收到了 ctrl+c 这个信号,也就是SIGINT。同时,程序也停止了运行。这里就有点问题了!!!。

上述的代码中,我们在注册好SIGINT信号之后,sleep 60秒的时间,在信号的处理回调函数里面,仅仅是做了一行信息的输出,并没有处理退出的操作。但是我们从结果中可以看到,当收到SIGINT这个信号的时候,程序输出信息之后就理解退出了。这是什么原因呢?

原因就是:信号的接收会唤醒sleep状态,也就导致了收到信号之后,本来sleep的状态被唤醒后,程序继续执行下去,执行完也就退出了。

具体文章可以参考:https://www.cnblogs.com/charlesblc/p/6277848.html