PHP进程间通信-信号

PHP进程间通信-信号

大家好,又见面了,我是全栈君。

点击上方“码农编程进阶笔记”,选择“置顶或者星标

优质文章第一时间送达!

(一)PHP进程间通信-信号
信号是一种系统调用。通常我们用的kill命令就是发送某个信号给某个进程的。具体有哪些信号可以在liunx/mac中运行kill -l查看。下面这个例子中,父进程等待5秒钟,向子进程发送sigint信号。子进程捕获信号,调信号处理函数处理。

代码演示

<?php
$childList = [];
$parentId = posix_getpid();

//信号处理函数
function signHandler($sign){
    $pid = posix_getpid();
    exit("process:{$pid},is killed,signal is {$sign}\n");
}

$pid = pcntl_fork();
if ($pid == -1){
    // 创建子进程失败
    exit("fork fail,exit!\n");
}elseif ($pid == 0){
    //子进程执行程序
    //注册信号处理函数
    declare(ticks = 10);
    pcntl_signal(SIGINT,"signHandler");//注册SIGINT信号处理函数
    $pid = posix_getpid();
    while (true){
        echo "child process {$pid},is running.......\n";
        sleep(1);
    }
}else{
    $childList[$pid] = 1;
    sleep(5);
    posix_kill($pid,SIGINT);//向指定进程发送一个信号
}

// 等待子进程结束
while(!empty($childList)){
    $pid = pcntl_wait($status);
    if ($pid > 0){
        unset($childList[$pid]);
    }
}

echo "The child process is killed by parent process {$parentId}\n";

运行结果
PHP进程间通信-信号
当父进程没有发送信号的时候,子进程会一直循环输出‘child process is running…’,父进程发送信号后,子进程在检查到有信号进来的时候调用对应的回调函数处理退出了子进程。

declare(ticks = 10)
这里的ticks=10,可以理解为程序执行10条低级语句后,检查看有没有未执行的信号,有的话就去处理。
关于declare(ticks = n)的详细讲解可以参考这篇文章

(二)初探

信号是一种软件中断,也是一种非常典型的异步事件处理方式。在NIX系统诞生的混沌之初,信号的定义是比较混乱的,而且最关键是不可靠,这是一个很严重的问题。所以在后来的POSIX标准中,对信号做了标准化同时也各个发行版的NIX也都提供大量可靠的信号。每种信号都有自己的名字,大概如SIGTERM、SIGHUP、SIGCHLD等等,在*NIX中,这些信号本质上都是整形数字(游有心情的可以参观一下signal.h系列头文件)。

信号的产生是有多种方式的,下面是常见的几种:

  • 键盘上按某些组合键,比如Ctrl+C或者Ctrl+D等,会产生SIGINT信号。

  • 使用posix kill调用,可以向某个进程发送指定的信号。

  • 远程ssh终端情况下,如果你在服务器上执行了一个阻塞的脚本,正在阻塞过程中你关闭了终端,可能就会产生SIGHUP信号。

  • 硬件也会产生信号,比如OOM了或者遇到除0这种情况,硬件也会向进程发送特定信号。

 而进程在收到信号后,可以有如下三种响应:

  • 直接忽略,不做任何反映。就是俗称的完全不鸟。但是有两种信号,永远不会被忽略,一个是SIGSTOP,另一个是SIGKILL,因为这两个进程提供了向内核最后的可靠的结束进程的办法。

  • 捕捉信号并作出相应的一些反应,具体响应什么可以由用户自己通过程序自定义。

  • 系统默认响应。大多数进程在遇到信号后,如果用户也没有自定义响应,那么就会采取系统默认响应,大多数的系统默认响应就是终止进程。

 

用人话来表达,就是说假如你是一个进程,你正在干活,突然施工队的喇叭里冲你嚷了一句:“吃饭了!”,于是你就放下手里的活儿去吃饭。你正在干活,突然施工队的喇叭里冲你嚷了一句:“发工资了!”,于是你就放下手里的活儿去领工资。你正在干活,突然施工队的喇叭里冲你嚷了一句:“有人找你!”,于是你就放下手里的活儿去看看是谁找你什么事情。当然了,你很任性,那是完全可以不鸟喇叭里喊什么内容,也就是忽略信号。也可以更任性,当喇叭里冲你嚷“吃饭”的时候,你去就不去吃饭,你去睡觉,这些都可以由你来。而你在干活过程中,从来不会因为要等某个信号就不干活了一直等信号,而是信号随时随地都可能会来,而你只需要在这个时候作出相应的回应即可,所以说,信号是一种软件中断,也是一种异步的处理事件的方式。

回到上文所说的问题,就是子进程在结束前,父进程就已经先调用了pcntl_waitpid(),导致子进程在结束后依然变成了僵尸进程。实际上在父进程不断while循环调用pcntl_waitpid()是个解决办法,大概代码如下:

$pid = pcntl_fork();
if( 0 > $pid ){
  exit('fork error.'.PHP_EOL);
} else if( 0 < $pid ) {
  // 在父进程中
  cli_set_process_title('php father process');
  // 父进程不断while循环,去反复执行pcntl_waitpid(),从而试图解决已经退出的子进程
  while( true ){
    sleep( 1 );
    pcntl_waitpid( $pid, &$status, WNOHANG );
  }
} else if( 0 == $pid ) {
  // 在子进程中
  // 子进程休眠3秒钟后直接退出
  cli_set_process_title('php child process');
  sleep( 20 );
  exit;
}

下图是运行结果:
PHP进程间通信-信号

解析一下这个结果,我先后三次执行了ps -aux | grep php去查看这两个php进程。
  • 第一次:子进程正在休眠中,父进程依旧在循环中。

  • 第二次:子进程已经退出了,父进程依旧在循环中,但是代码还没有执行到pcntl_waitpid(),所以在子进程退出后到父进程执行回收前这段空隙内子进程变成了僵尸进程。

  • 第三次:此时父进程已经执行了pcntl_waitpid(),将已经退出的子进程回收,释放了pid等资源。

 

但是这样的代码有一个缺陷,实际上就是子进程已经退出的情况下,主进程还在不断while pcntl_waitpid()去回收子进程,这是一件很奇怪的事情,并不符合社会主义主流价值观,不低碳不节能,代码也不优雅,不好看。所以,应该考虑用更好的方式来实现。那么,我们篇头提了许久的信号终于概要出场了。

现在让我们考虑一下,为何信号可以解决“不低碳不节能,代码也不优雅,不好看”的问题。子进程在退出的时候,会向父进程发送一个信号,叫做SIGCHLD,那么父进程一旦收到了这个信号,就可以作出相应的回收动作,也就是执行pcntl_waitpid(),从而解决掉僵尸进程,而且还显得我们代码优雅好看节能环保。

梳理一下流程,子进程向父进程发送SIGCHLD信号是对人们来说是透明的,也就是说我们无须关心。但是,我们需要给父进程安装一个响应SIGCHLD信号的处理器,除此之外,还需要让这些信号处理器运行起来,安装上了不运行是一件尴尬的事情。那么,在php里给进程安装信号处理器使用的函数是pcntl_signal(),让信号处理器跑起来的函数是pcntl_signal_dispatch()。

  • pcntl_signal(),安装一个信号处理器,具体说明是pcntl_signal     ( int $signo , callback $handler [, bool $restart_syscalls = true ] ),参数signo就是信号,callback则是响应该信号的代码段,返回bool值。

  • pcntl_signal_dispatch(),调用每个等待信号通过pcntl_signal()     安装的处理器,参数为void,返回bool值。

 

下面结合新引入的两个函数来解决一下楼上的丑陋代码:

$pid = pcntl_fork();
if( 0 > $pid ){
  exit('fork error.'.PHP_EOL);
} else if( 0 < $pid ) {
  // 在父进程中
  // 给父进程安装一个SIGCHLD信号处理器
  pcntl_signal( SIGCHLD, function() use( $pid ) {
    echo "收到子进程退出".PHP_EOL;
    pcntl_waitpid( $pid, $status, WNOHANG );
  } );
  cli_set_process_title('php father process');
  // 父进程不断while循环,去反复执行pcntl_waitpid(),从而试图解决已经退出的子进程
  while( true ){
    sleep( 1 );
    // 注释掉原来老掉牙的代码,转而使用pcntl_signal_dispatch()
    //pcntl_waitpid( $pid, &$status, WNOHANG );
    pcntl_signal_dispatch();
  }
} else if( 0 == $pid ) {
  // 在子进程中
  // 子进程休眠3秒钟后直接退出
  cli_set_process_title('php child process');
  sleep( 20 );
  exit;
}

运行结果如下:
PHP进程间通信-信号
PHP进程间通信-信号

PHP进程间通信-信号

PHP进程间通信-信号

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/112018.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Adobe Dreamweaver 2021下载安装教程

    Adobe Dreamweaver 2021下载安装教程软件介绍AdobeDreamweaver2021是专业的网站设计软件,使用可为处理各种Web文档提供灵活的环境。Dreamweaver2021一款非常受欢迎的网页设计软件,是该系列的全新版本,可以帮助广大学生、程序员制作出精美的网页,在全新的Dreamweaver2021版本中,在其优秀的功能上带来了更多的改进和优化,拥有无缝实时视图编辑功能,在以往用户需要切换到单独的编辑模式来预览网站,现在仅需一键即可预览和更改网页,还支持Windows的多显示器方案,为用户带来了更加整洁主界面,并且修改了十多个

  • 《老友记》典故集解 Season 1-10

    《老友记》典故集解 Season 1-10第一季第一集Mr.PotatoHead瑞秋和众人谈到了她逃婚的原因,她说这是因为她突然发现她的未婚夫巴里医生长得活像“薯头先生(Mr.PotatoHead)”,这是在美国家喻户晓的卡通人物。如果大家看过《玩具总动员(ToyStory)》,就会在里面发现他和他的夫人“薯头太太(Mrs.PotatoHead)”叽叽歪歪,经常批评这、批评那的形象。尽管“薯头先生”很…

  • k8s(七)Pod调度[通俗易懂]

    k8s(七)Pod调度[通俗易懂]k8s概述定向调度亲和性调度污点和容忍Pod的调度概述在默认情况下,一个Pod在哪个Node节点上运行,是由Scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的。但是在实际使用中,这并不满足需求,因为很多情况下,我们想控制某些Pod到达某些节点上,那么应该怎么做?这就要求了解kubernetes对Pod的调度规则,kubernetes提供了四大类调度方式。自动调度:运行在哪个Node节点上完全由Scheduler经过一系列的算法计算得出。定向调度:NodeName、NodeS

  • 开机弹出sysloader.exe应用程序错误

    开机弹出sysloader.exe应用程序错误
    反间谍专家查杀硬盘
    黄山ie修复专家修复ie
    搜索电脑上sysloader的文件,找到后删除
    就ok

    2022年10月24日
  • SecureCRTPortable的安装和使用(图文详解)

    SecureCRTPortable的安装和使用(图文详解)    不多说,直接上干货!    玩玩这个远程连接软件,是个绿色软件。      别人已经做好了的。       解压之后,  下面,软件展示下,                这会默认去打开,          为了,方便,使用,放到桌面,作为快捷方式    …

  • STM32F407 + LAN8720A + LWIP 实现TCP服务器

    STM32F407 + LAN8720A + LWIP 实现TCP服务器STM32F407+LAN8720A+LWIP实现TCP客户端环境说明:开发板:某宝买的,STM32F407IGSTM32CUBEMX5.6HALLibVersion1.25(一)配置时钟(二)配置调试串口(三)配置以太网ETH(1)基础配置顺序依次说明:LAN8720A使用的是RMII接口进行配置寄存器自动重连使能MAC地址LAN8720A的物理地址(类似IIC的从设备地址),可配置为0或者1,由LAN8720A的RXER/PHYAD0引脚控制

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号