通过几个简单的例子,来帮助理解php的信号系统
singnal_fun.php是公用函数库,代码如下
<?php
// 设置信号处理函数
function signal_handler($signo) {
switch ($signo) {
case SIGTERM:
// 处理SIGTERM信号
tlog("Caught SIGTERM...exit...\n");
exit;
break;
case SIGHUP:
//处理SIGHUP信号
tlog("Caught SIGHUP...\n");
break;
case SIGUSR1:
tlog("Caught SIGUSR1...\n");
break;
default:
// 处理所有其他信号
}
}
// 写日志
function tlog($message, $print=1) {
$message = posix_getpid() . '_' . $message . "\n";
if ($print) {
echo $message;
} else {
error_log(posix_getpid() . '_' . $message . "\n", 3, '/var/www/log.log');
}
}
function _system($script) {
return system("/usr/local/php/bin/php /var/www/{$script} >> /var/www/system.log");
}
功能:父进程先开启子进程,然后通过信号传递,关闭子进程,一直循环
<?php
include_once './signal_fun.php';
tlog('im start run');
declare(ticks=1);
// 开启一个信号监控
pcntl_signal(SIGTERM, 'signal_handler');
$myids = array();
while(1) {
for ($i = 0; $i < 5; ++$i) {
$mypid =pcntl_fork(); // 开启一个子进程,子进程和父进程同时从此处向下执行
if ($mypid < 0) { // 子进程启动失败
tlog('fork error!');
} elseif ($mypid == 0) { // 子进程中,读到的进程id为0
tlog('im child!');
sleep(1000); // 子进程挂起
exit;
} else { // 父进程读到子进程的pid
$myids[] = $mypid;
sleep(1);
}
}
sleep(10);
if ($myids) {
foreach ($myids as $key => $pid) {
tlog('kill '. $pid);
posix_kill($pid, SIGTERM); // 传递信号,kill 子进程
unset($myids[$key]);
sleep(2);
}
}
}
可以看到打印的日志
30834_im start run
30835_im child!
30836_im child!
30837_im child!
30838_im child!
30839_im child!
30834_kill 30835
30835_Caught SIGTERM...exit...
30834_kill 30836
30836_Caught SIGTERM...exit...
30834_kill 30837
30837_Caught SIGTERM...exit...
30834_kill 30838
30838_Caught SIGTERM...exit...
30834_kill 30839
30839_Caught SIGTERM...exit...
30860_im child!
30863_im child!
30866_im child!
30869_im child!
30872_im child!
先开启5个子进程,再杀死5个子进程,再开启...以此循环。
例2
功能:在后台挂起需要执行的脚本,任意脚本退出之后会马上重新开启一个
<?php
include_once './signal_fun.php';
$pids = array(); // 子进程pid容器
$end_pid = 0; // 当前接收到的结束进程的pid
while(1) {
for ($i = 1; $i <= 3; $i++) {
if ($end_pid) {
if ($i != current(array_diff(array(1,2,3), $pids))) { // 过滤掉正在运行的脚本
continue;
}
}
$mypid = pcntl_fork();
if ($mypid < 0) {
tlog('fork error!');
} elseif ($mypid == 0) { // child
_system('t'.$i.'.php'); // 挂起脚本
exit;
} else { // parent
$pids[$mypid] = $i; // 将正在运行的脚本的编号与子进程id压入数组
}
}
$end_pid = pcntl_wait($status); // 挂起父进程,等待接收子进程结束的信号
tlog('----接收到子进程-----' . $end_pid . '====运行结束');
unset($pids[$end_pid]);
}
父进程debug信息
32101_----接收到子进程-----32104====运行结束
32101_----接收到子进程-----32106====运行结束
32101_----接收到子进程-----32102====运行结束
32101_----接收到子进程-----32111====运行结束
32101_----接收到子进程-----32114====运行结束
看看日志log信息
tail -f /var/www/system.log
32107_t1 runnion
32109_t2 runnion
32110_t3 runnion
32113_t2 runnion
32116_t3 runnion
32119_t1 runnion
32122_t2 runnion
32125_t3 runnion
32128_t2 runnion
32131_t3 runnion
32134_t2 runnion
32137_t3 runnion
32140_t1 runnion
32143_t3 runnion
32146_t2 runnion
32149_t3 runnion
32152_t2 runnion
32155_t3 runnion
可以看到一直在开启这3个脚本。
再来看看进程信息
[root@192.168.64.90 /var/www]# ps -ef | grep signal3.php
root 32101 30341 0 14:44 pts/0 00:00:00 php signal3.php
root 32346 32101 0 14:46 pts/0 00:00:00 php signal3.php
root 32349 32101 0 14:46 pts/0 00:00:00 php signal3.php
root 32352 32101 0 14:46 pts/0 00:00:00 php signal3.php
可以看出父进程开启了3个子进程,用于运行对应的脚本
[root@192.168.64.90 /var/www]# ps -ef | grep /var/www
root 32463 32462 0 14:47 pts/0 00:00:00 sh -c /usr/local/php/bin/php /var/www/t1.php >> /var/www/system.log
root 32464 32463 0 14:47 pts/0 00:00:00 /usr/local/php/bin/php /var/www/t1.php
root 32477 32476 0 14:47 pts/0 00:00:00 sh -c /usr/local/php/bin/php /var/www/t2.php >> /var/www/system.log
root 32478 32477 0 14:47 pts/0 00:00:00 /usr/local/php/bin/php /var/www/t2.php
root 32480 32479 0 14:47 pts/0 00:00:00 sh -c /usr/local/php/bin/php /var/www/t3.php >> /var/www/system.log
root 32481 32480 0 14:47 pts/0 00:00:00 /usr/local/php/bin/php /var/www/t3.php
可以看到这3个脚本一直在后台运行着,kill其中一个之后,父进程like又会重新开启一个新的进程,这在需要运行daemon脚本时非常有用