PHP进程退出信号_一文吃透 PHP 进程信号处理

背景

前两周老大给安排了一个任务,写一个监听信号的包。因为我司的项目是运行在容器里边的,每次上线,需要重新打包镜像,然后启动。在重新打包之前,Dokcer会先给容器发送一个信号,然后等待一段超时时间(默认10s)后,再发送SIGKILL信号来终止容器

现在有一种情况,容器中有一个常驻进程,该常驻进程的任务是不断的消费队列里的消息。假设现在要上线,需要关杀掉容器,Docker给容器里跑的常驻进程发送一个信号,告诉它我10s后会将你关闭,假设现在已经过了9秒,常驻进程刚从队列中取出一条消息,1s内还没将后续逻辑执行完,进程就已经被杀了,此时这条消息就丢失了,且可能会产生脏数据

上边就是这次任务的背景,需要通过监听信号来决定后续如何操作。对于上边这种情况,当常驻进程收到Docker发送的关闭信号时,将该进程阻塞即可,一直sleep,直到杀掉容器。OK,清楚背景之后,下边就介绍一下PHP中的信号(后边会再整理一篇这个包如何写,并将包发布到https://packagist.org/,供需要的小伙伴使用)

一、在Linux操作系统中有哪些信号

1、简单介绍信号

信号是事件发生时对进程的通知机制,有时又称为软件中断。一个进程可以向另一个进程发送信号,比如子进程结束时都会向父进程发送一个SIGCHLD(17号信号)来通知父进程,所以有时信号也被当作一种进程间通信的机制。

在linux系统下,通常我们使用 kill -9 XXPID来结束一个进程,其实这个命令的实质就是向某进程发送SIGKILL(9号信号),对于在前台进程我们通常用Ctrl+c快捷键来结束运行,该快捷键的实质是向当前进程发送SIGINT(2号信号),而进程收到该信号的默认行为是结束运行

2、常用信号

下边这些信号,可以使用kill -l命令进行查看

576ae95f1c1ec9ece41cf90e3c99a1bc.png

下边介绍几个比较重要且常用的信号:

信号名

信号值

信号类型

信号描述

SIGHUP

1

终止进程(终端线路挂断)

本信号在用户终端连接(正常或非正常、结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联

SIGQUIT

2

终止进程(中断进程)

程序终止(interrupt、信号, 在用户键入INTR字符(通常是Ctrl-C、时发出

SIGQUIT

3

建立CORE文件终止进程,并且生成CORE文件

进程,并且生成CORE文件SIGQUIT 和SIGINT类似, 但由QUIT字符(通常是Ctrl-、来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信 号

SIGFPE

8

建立CORE文件(浮点异常)

SIGFPE 在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢 出及除数为0等其它所有的算术的错误

SIGKILL

9

终止进程(杀死进程)

SIGKILL 用来立即结束程序的运行. 本信号不能被阻塞, 处理和忽略

SIGSEGV

11

SIGSEGV 试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据

SIGALRM

14

终止进程(计时器到时)

SIGALRM 时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该信号

SIGTERM

15

终止进程(软件终止信号)

SIGTERM 程序结束(terminate、信号, 与SIGKILL不同的是该信号可以被阻塞和处理. 通常用来要求程序自己正常退出. shell命令kill缺省产生这个信号

SIGCHLD

17

忽略信号(当子进程停止或退出时通知父进程)

SIGCHLD 子进程结束时, 父进程会收到这个信号

SIGVTALRM

26

终止进程(虚拟计时器到时)

SIGVTALRM 虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间

SIGIO

29

忽略信号(描述符上可以进行I/O)

SIGIO 文件描述符准备就绪, 可以开始进行输入/输出操作

二、PHP中处理信号相关函数

PHP的pcntl扩展以及posix扩展为我们提供了若干操作信号的方法(若想使用这些函数,需要先安装这几个扩展)

下边具体介绍几个我在本次任务中用到的方法:

declare

declare结构用来设定一段代码的执行指令。declare的语法和其它流程控制结构相似

declare (directive)

statement

复制代码

directive部分允许设定declare代码段的行为。目前只认识两个指令:ticks和encoding。declare代码段中的 statement部分将被执行——怎样执行以及执行中有什么副作用出现取决于directive中设定的指令

Ticks

Tick(时钟周期)是一个在declare代码段中解释器每执行N条可计时的低级语句就会发生的事件N的值是在declare 中的directive部分用ticks=N来指定的。不是所有语句都可计时。通常条件表达式和参数表达式都不可计时。在每个tick中出现的事件是由register_tick_function()来指定的,注意每个 tick 中可以出现多个事件

更详细的内容,可查看官方文档:https://www.php.net/manual/zh/control-structures.declare.php

declare(ticks=1);//每执行一条时,触发register_tick_function()注册的函数

$a=1;//在注册之前,不算

function test(){//定义一个函数

echo "执行\n";

}

register_tick_function('test');//该条注册函数会被当成低级语句被执行

for($i=0;$i<=2;$i++){//for算一条低级语句

$i=$i;//赋值算一条

}

输出:六个“执行”

复制代码

pcntl_signal

pcntl_signal,安装一个信号处理器

pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls = true ] ) : bool

复制代码

函数pcntl_signal()为signo指定的信号安装一个新的信号处理器

declare(ticks = 1);

pcntl_signal(SIGINT,function(){

echo "你按了Ctrl+C".PHP_EOL;

});

while(1){

sleep(1);//死循环运行低级语句

}

输出:当按Ctrl+C之后,会输出“你按了Ctrl+C”

复制代码

posix_kill

posix_kill,向进程发送一个信号

posix_kill ( int $pid , int $sig ) : bool

复制代码

第一个参数为进程ID,第二个参数为你要发送的信号

a.php

declare(ticks = 1);

echo getmypid();//获取当前进程id

pcntl_signal(SIGINT,function(){

echo "你给我发了SIGINT信号";

});

while(1){

sleep(1);

}

b.php

posix_kill(执行1.php时输出的进程id, SIGINT);

复制代码

pcntl_signal_dispatch

pcntl_signal_dispatch,调用等待信号的处理器

pcntl_signal_dispatch ( void ) : bool

复制代码

函数pcntl_signal_dispatch()调用每个等待信号通过pcntl_signal()安装的处理器

echo "安装信号处理器...\n";

pcntl_signal(SIGHUP,  function($signo) {

echo "信号处理器被调用\n";

});

echo "为自己生成SIGHUP信号...\n";

posix_kill(posix_getpid(), SIGHUP);

echo "分发...\n";

pcntl_signal_dispatch();

echo "完成\n";

?>

输出:

安装信号处理器...

为自己生成SIGHUP信号...

分发...

信号处理器被调用

完成

复制代码

pcntl_async_signals()

异步信号处理,用于启用无需 ticks (这会带来很多额外的开销)的异步信号处理。(PHP>=7.1)

pcntl_async_signals(true); // turn on async signals

pcntl_signal(SIGHUP,  function($sig) {

echo "SIGHUP\n";

});

posix_kill(posix_getpid(), SIGHUP);

输出:

SIGHUP

复制代码

三、PHP中处理信号量的方式

前边我们知道我们可以通过declare(ticks=1)和pcntl_signal组合的方式监听信号,即每一条PHP低级语句,就会检查一次当前进程是否有未处理的信号,这其实是十分耗性能的。

pcntl_signal的实现原理是,触发信号后先将信号加入一个队列中。然后在PHP的ticks回调函数中不断检查是否有信号,如果有信号就执行PHP中指定的回调函数,如果没有则跳出函数。

PHP_MINIT_FUNCTION(pcntl)

{

php_register_signal_constants(INIT_FUNC_ARGS_PASSTHRU);

php_pcntl_register_errno_constants(INIT_FUNC_ARGS_PASSTHRU);

php_add_tick_function(pcntl_signal_dispatch TSRMLS_CC);

return SUCCESS;

}

复制代码

在PHP5.3之后,有了pcntl_signal_dispatch函数。这个时候将不在需要declare,只需要在循环中增加该函数,就可以调用信号通过了:

echo getmypid();//获取当前进程id

pcntl_signal(SIGUSR1,function(){

echo "触发信号用户自定义信号1";

});

while(1){

pcntl_signal_dispatch();

sleep(1);//死循环运行低级语句

}

复制代码

大家都知道PHP的ticks=1表示每执行1行PHP代码就回调此函数。实际上大部分时间都没有信号产生,但ticks的函数一直会执行。如果一个服务器程序1秒中接收1000次请求,平均每个请求要执行1000行PHP代码。那么PHP的pcntl_signal,就带来了额外的 1000 * 1000,也就是100万次空的函数调用。这样会浪费大量的CPU资源。比较好的做法是去掉ticks,转而使用pcntl_signal_dispatch,在代码循环中自行处理信号。

pcntl_signal_dispatch 函数的实现:

void pcntl_signal_dispatch()

{

//.... 这里略去一部分代码,queue即是信号队列

while (queue) {

if ((handle = zend_hash_index_find(&PCNTL_G(php_signal_table), queue->signo)) != NULL) {

ZVAL_NULL(&retval);

ZVAL_LONG(&param, queue->signo);

/* Call php signal handler - Note that we do not report errors, and we ignore the return value */

/* FIXME: this is probably broken when multiple signals are handled in this while loop (retval) */

call_user_function(EG(function_table), NULL, handle, &retval, 1, &param TSRMLS_CC);

zval_ptr_dtor(&param);

zval_ptr_dtor(&retval);

}

next = queue->next;

queue->next = PCNTL_G(spares);

PCNTL_G(spares) = queue;

queue = next;

}

}

复制代码

但是上边这种,也有个恶心的地方就是,它得放在死循环中。PHP7.1之后出来了一个完成异步的信号接收并处理的函数:

pcntl_async_signals

//a.php

echo getmypid();

pcntl_async_signals(true);//开启异步监听信号

pcntl_signal(SIGUSR1,function(){

echo "触发信号";

posix_kill(getmypid(),SIGSTOP);

});

posix_kill(getmypid(),SIGSTOP);//给进程发送暂停信号

//b.php

posix_kill(文件1进程, SIGCONT);//给进程发送继续信号

posix_kill(文件1进程, SIGUSR1);//给进程发送user1信号

复制代码

通过pcntl_async_signals方法,就不用再写死循环了。

监听信号的包:

https://github.com/Rain-Life/monitorSignal

复制代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/289777.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GitHub Copilot 现已登陆 Visual Studio!

激动人心的好消息来了&#xff0c;GitHub 在3月29日发布博客&#xff0c;宣布 Github Copilot 现在可以在 Visual Studio 中使用。我们知道 Visual Studio 的 IntelliCode 本身已经很智能了, 现在又迎来了 Copilot, 编程体验将进入新的篇章。如何安装? 首先&#xff0c;您…

iOS 9音频应用播放音频之音量设置与声道设置

iOS 9音频应用播放音频之音量设置与声道设置 iOS 9音频应用音量设置 音量又称响度、音强&#xff0c;是指人耳对所听到的声音大小强弱的主观感受&#xff0c;其客观评价尺度是声音的振幅大小。在iOS 9音频应用的应用中&#xff0c;经常会出现播放的音乐音量过大或者过小。此时i…

php fpm工作原理,什么是phpfpm的工作原理?

什么是phpfpm的工作原理&#xff1f;发布时间&#xff1a;2020-07-13 15:12:53来源&#xff1a;亿速云阅读&#xff1a;181作者&#xff1a;Leah什么是phpfpm的工作原理&#xff1f;针对这个问题&#xff0c;这篇文章详细介绍了相对应的分析和解答&#xff0c;希望可以帮助更多…

C#对象映射器之Mapster

简介Mapster是一个快&#xff0c;小巧&#xff0c;功能强大的对象映射.Net框架例子我有两个Model类且他们的属性一致&#xff0c;我们将 SourceObjectTest赋值给DestObjectTest该怎么做&#xff1f;SourceObjectTest sourceObject new SourceObjectTest(); sourceObject.Name …

如何关闭Struts2的webconsole.html

出于安全目的&#xff0c;在禁用了devMode之后&#xff0c;仍然不希望其他人员看到webconsole.html页面&#xff0c;则可以直接删除webconsole.html 的源文件&#xff0c; 它的位置存在于&#xff1a; 我们手工删除 struts2-core-*.jar\org\apache\struts2\interceptor\debuggi…

UIView 的基础

UIView•什么是控件&#xff1f;-屏幕上的所有UI元素都叫做控件&#xff0c;也有人叫做视图、组件-按钮&#xff08;UIButton&#xff09;、文本&#xff08;UILabel&#xff09;都是控件•控件的共同属性有哪些&#xff1f;-尺寸-位置-背景色-......-•苹果将控件的共同属性都…

JS

为什么80%的码农都做不了架构师&#xff1f;>>> function getQueryString(name) {var reg new RegExp("(^|&)" name "([^&]*)(&|$)"),r window.location.search.substr(1).match(reg);if(r ! null) {return unescape(r[2]); }r…

ssh公钥免密码登录

2019独角兽企业重金招聘Python工程师标准>>> ssh 无密码登录要使用公钥与私钥。linux下可以用用ssh-keygen生成公钥/私钥对&#xff0c;下面我以CentOS为例。 有机器A(192.168.1.155)&#xff0c;B(192.168.1.181)。现想A通过ssh免密码登录到B。 首先以root账户登陆…

Spring4Shell的漏洞原理分析

Spring框架最新的PoC这两天出来的一个RCE漏洞&#xff0c;但是有以下的条件限制才行&#xff1a;必须是jdk9及以上必须是部署在tomcat的应用是springmvc的或者webflux的应用具体的可以查看spring官方&#xff1a;https://spring.io/blog/2022/03/31/spring-framework-rce-early…

php 点对点,浅析点对点(End-to-End)的场景文字识别

一、背景随着智能手机的广泛普及和移动互联网的迅速发展&#xff0c;通过手机等移动终端的摄像头获取、检索和分享资讯已经逐步成为一种生活方式。基于摄像头的(Camera-based)的应用更加强调对拍摄场景的理解。通常&#xff0c;在文字和其他物体并存的场景&#xff0c;用户往往…

【ArcGIS遇上Python】Python实现Modis NDVI批量化月最大合成

「 刘一哥GIS」CSDN专业技术博文专栏目录索引https://geostorm.blog.csdn.net/article/details/113732454 最大合成法(MVC)可以在Envi中的Band Math中进行,式子是B1>B2,但是无法批量化;本文实现在ArcGIS中利用Python代码批量进行,如下: 用到的Modis NDVI数据是在MRT…

cad2016中选择全图字体怎么操作_打开CAD图纸字体丢失、重新选择怎么办?这样设置,一辈子用的到...

AutoCAD图纸本身就有着比较特殊的个性&#xff0c;难编辑难打开&#xff0c;时不时的还会来个乱码、字体缺失&#xff0c;甚至有的时候还提示我们进行字体的重新选择&#xff0c;应该怎么解决呢&#xff1f;虽然是个很经常遇见的问题&#xff0c;很多的小伙伴还是不知道如何解决…

MassTransit - .NET Core 的分布式应用程序框架

简介MassTransit 是一个免费的、开源的.NET 分布式应用程序框架。MassTransit 使创建应用程序和服务变得容易&#xff0c;这些应用程序和服务利用基于消息的松散耦合异步通信来实现更高的可用性、可靠性和可扩展性特点•易于使用和理解的 API&#xff0c;让您专注于解决业务问题…

mongo学习笔记(二):聚合,游标

一、聚合 <1> Count 1.db.person.count() 2.db.person.count({"age":20}) <2> Distinct db.person.distinct("age")//指定了谁&#xff0c;谁就不能重复 <3> Group key&#xff1a;这个就是分组的key&#xff0c;我们这里是对年龄分组。…

Java实验二猜数字游戏,JAVA-第2周实验-猜数字游戏

JAVA--第2周实验--猜数字游戏/* (程序头部注释开始)* 程序的版权和版本声明部分* Copyright (c) 2011, 烟台大学计算机学院学生* All rights reserved.* 文件名称&#xff1a;猜数字游戏* 作 者&#xff1a; 雷恒鑫* 完成日期&#xff1a; 2012 年 09 月 07 日* 版 本 号&#…

Xamarin效果第十五篇之自定义CheckBox

在上一篇中使用Xamarin玩耍了GIS加载高德和百度;这不这两天又抽空完善了一下;自定义了一下CheckBox;来看看最终效果:关于实现咱就是直接自定义ContentView:后台无非就是一堆的依赖属性(和WPF真像):后台定义的Command为了支持选中状态:‍再来看看具体使用:<CustomViews:Image…

java爬虫工具xpath提取,2020-07-16--爬虫数据提取--xpath

xpath全称 XML Path Language 是一门在XML文档中 查找信息的语言 最初是用来搜寻XML文档的 但是它同样适用于HTML文档的搜索XPath 的选择功能十分强大&#xff0c;它提供了非常简洁的路径选择表达式&#xff0c;另外还提供了超过100个内置函数&#xff0c;用于字符串&#xff0…

ant压缩在哪卸载_反病毒软件这么多,到底哪一款适合你

记得大学读书的时候,买了电脑,第一件事情就是卸载windows自带的杀毒软件,然后装上自己心仪的杀毒软件,可是市面上杀毒软件这么多,哪一款适合你呢?我找了市面上排名最靠前的三款杀毒软件,让我们看看他们孰胜孰劣,还是各有千秋.他们分别是腾讯的电脑管家,金山毒霸,360安全卫士.这…

.NetCore使用NETCore.MailKit发送邮件

前言平时工作中很少用到需要发邮件的功能&#xff0c;所以邮件这块的功能也没有太过关注。近期有一个项目需要接收用户的反馈&#xff0c;上边决定使用邮件&#xff0c;直接将反馈信息发送给领导&#xff0c;也就有了这篇文章。实现发邮件不难&#xff0c;但是开发中遇到了一个…

哄媳妇

1、如果你的女人在你面前哭了&#xff0c;无论什么原因&#xff0c;请抱紧她&#xff0c;再反抗也要抱紧&#xff0c;趴在桌子上永远没有在你怀里安心&#xff1b; 2、如果你的女人指出了你的不是&#xff0c;请不要总是嫌她唠叨&#xff0c;若不是因为在乎她不会说你&#xff…