系统编程-信号
2020-12-16 本文已影响0人
aoshi
信号是事件发生时对进程的通知机制,有时又称为软件中断。一个进程可以向另一个进程发送信号,比如子进程结束时都会向父进程发送一个SIGCHLD(17号信号)来通知父进程,所以有时信号也被当作一种进程间通信的机制。
image.png在linux系统下,通常我们使用 kill -9 XXPID来结束一个进程,其实这个命令的实质就是向某进程发送 SIGKILL(9号信号),对于在前台运行的程序我们通常用 Ctrl + c 快捷键来结束运行,该快捷键的实质是向当前进程发送 SIGINT (2号信号),而进程收到该信号的默认行为是结束运行。我们可以用命令 kill -l 来查看系统的信号列表:
其中1 ~ 31 号信号为标准信号或者传统信号,而大于31号信号为实时信号,这里我们主要介绍 标准信号。进程收到一个信号时,视信号的不同,有以下几种不同的行为:
1)忽略信号,进程就像没收到过信号一样,比如父进程收到子进程发送的 SIGCHLD 信号
2)结束进程, 比如进程收到 SIGINT (Ctrl + c) 信号
3)暂停运行
4)从之前的暂停状态恢复运行
PHP的pcntl扩展以及posix扩展为我们提供了若干操作信号的方法:
pcntl_signal_dispatch — 调用等待信号的处理器
pcntl_signal_get_handler — Get the current handler for specified signal
pcntl_signal — 安装一个信号处理器
pcntl_sigprocmask — 设置或检索阻塞信号
pcntl_sigtimedwait — 带超时机制的信号等待
pcntl_sigwaitinfo — 等待信号
posix_kill — 向一个进程发送信号
pcntl_signal 方法可以让我们自定义进程对信号的处理动作,但是在linux系统中,SIGKILL(9号信号)和 SIGSTOP (19号信号)这两个信号是无法被我们自己捕获和处理的,SIGKILL总是会结束进程运行,SIGSTOP总是能暂停进程。
bool pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls = true ] )
signo
信号编号
handler
信号处理器可以是用户创建的函数或方法的名字,也可以是系统常量 SIG_IGN(译注:忽略信号处理程序)或SIG_DFL(默认信号处理程序)
在PHP中,有自己触发信号回调的机制
PCNTL现在使用了ticks作为信号处理的回调机制,ticks在速度上远远超过了之前的处理机制。
这个变化与“用户ticks”遵循了相同的语义。您可以使用declare() 语句在程序中指定允许发生回调的位置。这使得我们对异步事件处理的开销最小化。
在编译PHP时 启用pcntl将始终承担这种开销,不论您的脚本中是否真正使用了pcntl。 PHP 4.3.0使用ticks作为信号处理回调机制,这比以前的机制快了很多。
这个变化与 "用户ticks" 遵循了相同的语义。您可以使用declare() 语句在程序中指定允许发生回调的位置。
demo
<?php
class pcntl{
public function do_reap() {
echo 'this is over with null' . PHP_EOL;
exit('this is over');
}
/**
* 测试捕获kill信号
* */
public function testKill() {
//安装信号处理器 Ctrl + C
//pcntl_signal(SIGINT, array(&$this,"cron_JIGUANG_push_ctrl_c"));
//安装信号处理器 kill
//pcntl_signal(15, array(&$this,"cron_JIGUANG_push_kill"));
//安装信号处理器 Ctrl + C
pcntl_signal(SIGINT, function(){
echo "捕获到了 SIGINT 信号" . PHP_EOL;
}); //
//安装信号处理器 kill
pcntl_signal(15, array(&$this,"do_reap"));
for($i = 0;$i<30;$i++) {
echo $i . PHP_EOL;
sleep(3);
//调用等待信号的处理器 调用每个通过pcntl_signal() 安装的等待信号的处理器。
pcntl_signal_dispatch();
}
}
/**
* 注册信号回调不存在方法时处理逻辑
* */
public function __call($name, $arguments) {
$this->doKill($name);
}
/**
* 添加信号处理程序
* @param string $name 信号标识
* */
public function doKill($name) {
//log
CronKillLogModel::gi()->add(array('kill_signal'=>$name,'create_time'=>date('Y-m-d H:i:s')));
exit('this is over');
}
}
>
注意:
这个程序会再执行状态,当我们按下 Ctrl + c 时,就给程序发送了一个 SIGINT 信号,但由于我们自定义了信号处理,所以这时不会结束进程,而是执行信号接收回调函数。