php进程通信-进程信号

快一个月没发博文了,之前都在深入研究php多进程tcp服务器,结果到现在也没搞出一个完美的解决方案,所以还是先发下这个月学到的东西吧


注意:本文所有内容均在linux环境下

一:进程信号对照

在php进程信号常量中,有定义以下常量(该表格参考网上的,不完整)

信号名信号值信号类型信号说明
SIGHUP1终止进程(终端线路挂断)本信号在用户终端连接(正常或非正常、结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联.
SIGINT2终止进程(中断进程)程序终止(interrupt、信号, 在用户键入INTR字符(通常是Ctrl-C、时发出
SIGQUIT3建立CORE文件终止进程,并且生成CORE文件SIGQUIT 和SIGINT类似, 但由QUIT字符(通常是Ctrl-、来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信 号.
SIGILL4建立CORE文件(非法指令)SIGILL 执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出时也有可能产生这个信号.
SIGTRAP5建立CORE文件(跟踪自陷)SIGTRAP 由断点指令或其它trap指令产生. 由debugger使用.
SIGABRT6
SIGABRT 程序自己发现错误并调用abort时产生.
SIGIOT6建立CORE文件(执行I/O自陷)SIGIOT 在PDP-11上由iot指令产生, 在其它机器上和SIGABRT一样.
SIGBUS7建立CORE文件(总线错误)SIGBUS 非法地址, 包括内存地址对齐(alignment、出错. eg: 访问一个四个字长的整数, 但其地址不是4的倍数.
SIGFPE8建立CORE文件(浮点异常)SIGFPE 在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢 出及除数为0等其它所有的算术的错误.
SIGKILL9终止进程(杀死进程)SIGKILL 用来立即结束程序的运行. 本信号不能被阻塞, 处理和忽略.
SIGUSR110终止进程(用户自定义信号1)SIGUSR1 留给用户使用
SIGSEGV11
SIGSEGV 试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据.
SIGUSR212终止进程(用户自定义信号2)SIGUSR2 留给用户使用
SIGPIPE13终止进程(向一个没有读进程的管道写数据)Broken pipe
SIGALRM14终止进程(计时器到时)SIGALRM 时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该信号.
SIGTERM15终止进程(软件终止信号)SIGTERM 程序结束(terminate、信号, 与SIGKILL不同的是该信号可以被阻塞和处理. 通常用来要求程序自己正常退出. shell命令kill缺省产生这个信号.
SIGCHLD17忽略信号(当子进程停止或退出时通知父进程)SIGCHLD 子进程结束时, 父进程会收到这个信号.
SIGCONT18忽略信号(继续执行一个停止的进程)SIGCONT 让一个停止(stopped、的进程继续执行. 本信号不能被阻塞. 可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作. 例如, 重新显示提示符
SIGSTOP19停止进程(非终端信号)SIGSTOP 停止(stopped、进程的执行. 注意它和terminate以及interrupt的区别: 该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略.
SIGTSTP20停止进程(终端信号)SIGTSTP 停止进程的运行, 但该信号可以被处理和忽略. 用户键入SUSP字符时 (通常是Ctrl-Z、发出这个信号
SIGTTIN21停止进程(后端进程读终端)SIGTTIN 当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN 信号. 缺省时这些进程会停止执行.
SIGTTOU22停止进程(后端进程写终端)SIGTTOU 类似于SIGTTIN, 但在写终端(或修改终端模式、时收到.
SIGURG23忽略信号(I/O紧急信号)SIGURG 有”紧急”数据或out-of-band数据到达socket时产生.
SIGXCPU24终止进程(CPU实现超时)SIGXCPU 超过CPU时间资源限制. 这个限制可以由getrlimit/setrlimit来读取/ 改变
SIGXFSZ25终止进程(文件长度过长)SIGXFSZ 超过文件大小资源限制.
SIGVTALRM26终止进程(虚拟计时器到时)SIGVTALRM 虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间.
SIGPROF27终止进程(统计分布图用计时器到时)SIGPROF 类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的 时间.
SIGWINCH28忽略信号(窗口大小发生变化)SIGWINCH 窗口大小改变时发出.
SIGIO29忽略信号(描述符上可以进行I/O)SIGIO 文件描述符准备就绪, 可以开始进行输入/输出操作.
SIGPWR30
SIGPWR Power failure


二:php基础进程相关函数

注意:(需要pcntl扩展支持)

具体相关函数可查看php手册:http://php.net/manual/zh/book.pcntl.php

1:declare(ticks=1);每执行一条php低级语句,则触发一次register_tick_function函数,并且每执行1条低级语句会检查一次该进程是否有未处理过的信号.,该函数是在php版本小于5.3,用于php进行php信号处理的函数,例如:

<?php
declare(ticks=1);//每执行一条时,触发register_tick_function()注册的函数
$a=1;//再注册之前,不记录
$a=1;//再注册之前,不记录
function test(){//定义一个函数
    echo "执行\n";
}
register_tick_function('test');//该条注册函数会被当成低级语句被执行
for($i=0;$i<=3;$i++){//for算一条低级语句
    $i=$i;//赋值算一条
}

在php7.2中,运行结果如下:

仙士可博客


2:pcntl_signal;注册一个信号处理函数,和declare(ticks=1)组合使用:

declare(ticks = 1);
pcntl_signal(SIGINT,function(){
    echo "触发信号Ctrl+c";
});
while(1){
    sleep(1);//死循环运行低级语句
}

当执行该脚本,再ctrl+c的时候,将会捕捉到该信号,并输出,如图:

仙士可博客


3:getmypid.获取当前进程id,posix_kill发送信号

为什么会拿这2个一起说呢?因为posix_kill函数如果需要发送信号,是需要进程id的,而getmypid(),则是获取当前进程id的函数,

以下是将上面的函数组合使用的例子:

<?php
//文件一
declare(ticks = 1);
echo getmypid();//获取当前进程id
pcntl_signal(SIGUSR1,function(){
    echo "触发信号用户自定义信号1";
});
while(1){
    sleep(1);//死循环运行低级语句
}
<?php
//文件二
posix_kill(文件一进程, SIGUSR1);

运行文件1结果:

仙士可博客

运行文件2之后文件1结果:

仙士可博客


4:到这之后,你可能会想到,declare每次运行一次低级语句,都会尝试执行2种结果,效率会不会很差呢?答案是会的,所以在php5.3之后,有了新的函数,那就是pcntl_signal_dispatch

pcntl_signal_dispatch: 调用等待信号的处理器,有了它,将不在需要declare,只需要在循环中增加该函数,就可以调用信号通过了:

<?php
echo getmypid();//获取当前进程id
pcntl_signal(SIGUSR1,function(){
    echo "触发信号用户自定义信号1";
});
while(1){
    pcntl_signal_dispatch();
    sleep(1);//死循环运行低级语句
}

结果和3同样

5:看到4,你可能会觉得,信号处理还是没有那么的智能,能不能不做死循环,就完成异步的信号接收并处理呢?在php7.1之后,有了新的信号处理函数:pcntl_async_signals,返回或设置是否异步信号处理:

<?php
//文件一
echo getmypid();
pcntl_async_signals(true);//设置异步信号
pcntl_signal(SIGUSR1,function(){//安装个user1信号处理函数
    echo "触发信号";
    posix_kill(getmypid(),SIGSTOP);
});
posix_kill(getmypid(),SIGSTOP);//给进程发送暂停信号
<?php
//文件2
posix_kill(文件1进程, SIGCONT);//给进程发送继续信号
posix_kill(文件1进程, SIGUSR1);//给进程发送user1信号

首先文件1运行,再给文件2运行之后,文件1的结果图:

仙士可博客

文件1进程strace 命令截图:

仙士可博客

可看到,进程休眠之后,被9271进程(文件2)唤醒之后并发送了siguse1信号,再然后输出了一段文字,再然后自己给自己发送了进程休眠信号,继续休眠


linux 查看进程命令.可看:

https://blog.csdn.net/liangzhao_jay/article/details/50457197


6:pcntl_alarm和创建一个计时器,在指定的秒数后向进程发送一个SIGALRM信号。每次对 pcntl_alarm()的调用都会取消之前设置的alarm信号。例如:

<?php

pcntl_signal(SIGALRM, function () {
    echo '定时到时' . PHP_EOL;
});

pcntl_alarm(5);
$i=0;
while(1){
    echo $i.PHP_EOL;$i++;
    pcntl_signal_dispatch();
    sleep(1);
}

仙士可博客


该函数使用场景之一:php熔断

<?php
pcntl_async_signals(true);
pcntl_signal(SIGALRM, function () {
    echo 'php处理超时' . PHP_EOL;
});

pcntl_alarm(30);
/*
 * 这里是一大段php处理函数
 * */
pcntl_alarm(-1);

先进行30秒的定时,当处理函数超过30秒时,将触发php处理超时函数,从而进行超时逻辑,当在30秒处理完毕时,php将关闭改定时信号,正常往下执行



三:其他

1:php进程信号中,无法捕获SIGKILL信号,该信号将会强制关闭进程





仙士可博客
请先登录后发表评论
  • 最新评论
  • 总共1条评论
仙士可博客

强壮的心:最近也在研究这个,可以研究一下swoole

2018-06-01 19:48:52 回复

  • 本站由白俊遥博客程序搭建
    © 2017-1-17 php20.cn 版权所有 ICP证:闽ICP备17001387号
  • 联系邮箱:1067197739@qq.com