unix 的信号signal常用于进程管理.

比如管理员或者操作系统通过向master进程实现重启和关闭服务.

master进程通过向worker进程发信号管理worker进程.

通常会在进程自定义信号处理函数,处理相关的逻辑.

自定义信号处理函数,从使用者的角度看,很简单,有点像快捷键的定制.

FPM 信号处理有以下几个特点:

master进程,不是直接处理信号,而是通过socketpair创建一个管道,把信号转换一个字符,写到管道里,master进程事件处理无限循环,读取到这个字符时,调用对应的函数.

socketpair,通常管道不同进程通信,而这里确是在同一个进程内部通信,左手交右手,感觉多此一举.

这样做的好处是: 避免信号处理函数与事件处理逻辑同时运行的情况.

注意worker 进程没有用到这个socketpair管道.

worker 进程的信号处理常见的方式,直接绑定处理函数.

处理过程: sig_soft_quit -> fpm_php_soft_quit -> fcgi_set_in_shutdown

fcgi_set_in_shutdown 函数很简单 就是设置in_shutdown这个全局的worker进程开关

worker进程无限循环时,每次都会检查这个开关, in_shutdown=1 时,跳出循环,优雅退出.

源码注释说明:

//fpm_signals.c

#include "fpm_config.h"

...

//整数数组,存放socketpair创建的管道两端文件句柄

static int sp[2];

...

//worker进程信号处理函数

static void sig_soft_quit(int signo) /* {{{ */

{

int saved_errno = errno;

/* closing fastcgi listening socket will force fcgi_accept() exit immediately */

close(0);

if (0 > socket(AF_UNIX, SOCK_STREAM, 0)) {

zlog(ZLOG_WARNING, "failed to create a new socket");

}

fpm_php_soft_quit();

errno = saved_errno;

}

//master进程信号处理函数

static void sig_handler(int signo) /* {{{ */

{

//C99 的数组初始化语法

//信号整数和字符的对应关系.

static const char sig_chars[NSIG + 1] = {

[SIGTERM] = 'T',

[SIGINT] = 'I',

[SIGUSR1] = '1',

[SIGUSR2] = '2',

[SIGQUIT] = 'Q',

[SIGCHLD] = 'C'

};

char s;

int saved_errno;

if (fpm_globals.parent_pid != getpid()) {

return;

}

saved_errno = errno;

s = sig_chars[signo];

//信号对应的字符写到管道

write(sp[1], &s, sizeof(s));

errno = saved_errno;

}

int fpm_signals_init_main() /* {{{ */

{

struct sigaction act;

//创建socketpair管道,管道两端的文件句柄fd 放在数组sp里

if (0 > socketpair(AF_UNIX, SOCK_STREAM, 0, sp)) {

zlog(ZLOG_SYSERROR, "failed to init signals: socketpair()");

return -1;

}

if (0 > fd_set_blocked(sp[0], 0) || 0 > fd_set_blocked(sp[1], 0)) {

zlog(ZLOG_SYSERROR, "failed to init signals: fd_set_blocked()");

return -1;

}

if (0 > fcntl(sp[0], F_SETFD, FD_CLOEXEC) || 0 > fcntl(sp[1], F_SETFD, FD_CLOEXEC)) {

zlog(ZLOG_SYSERROR, "falied to init signals: fcntl(F_SETFD, FD_CLOEXEC)");

return -1;

}

memset(&act, 0, sizeof(act));

act.sa_handler = sig_handler; //所有信号使用同一个处理函数

sigfillset(&act.sa_mask);

if (0 > sigaction(SIGTERM, &act, 0) ||

0 > sigaction(SIGINT, &act, 0) ||

0 > sigaction(SIGUSR1, &act, 0) ||

0 > sigaction(SIGUSR2, &act, 0) ||

0 > sigaction(SIGCHLD, &act, 0) ||

0 > sigaction(SIGQUIT, &act, 0)) {

zlog(ZLOG_SYSERROR, "failed to init signals: sigaction()");

return -1;

}

return 0;

}

int fpm_signals_init_child()

{

struct sigaction act, act_dfl;

memset(&act, 0, sizeof(act));

memset(&act_dfl, 0, sizeof(act_dfl));

act.sa_handler = &sig_soft_quit;

act.sa_flags |= SA_RESTART;

act_dfl.sa_handler = SIG_DFL; //系统默认动作

//worker 进程不使用socketpair创建的管道

close(sp[0]);

close(sp[1]);

if (0 > sigaction(SIGTERM, &act_dfl, 0) ||

0 > sigaction(SIGINT, &act_dfl, 0) ||

0 > sigaction(SIGUSR1, &act_dfl, 0) ||

0 > sigaction(SIGUSR2, &act_dfl, 0) ||

0 > sigaction(SIGCHLD, &act_dfl, 0) ||

0 > sigaction(SIGQUIT, &act, 0)) {

zlog(ZLOG_SYSERROR, "failed to init child signals: sigaction()");

return -1;

}

return 0;

}

int fpm_signals_get_fd()

{

return sp[0];

}

master 进程的信号被写到了管道,管道另一端的处理:

//fpm_events.c

static void fpm_got_signal(struct fpm_event_s *ev, short which, void *arg)

{

char c;

int res, ret;

int fd = ev->fd;

do {

do {

res = read(fd, &c, 1);

} while (res == -1 && errno == EINTR);

if (res <= 0) {

if (res < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {

zlog(ZLOG_SYSERROR, "unable to read from the signal pipe");

}

return;

}

//依据读取到的字符做处理

switch (c) {

...

case 'Q' : /* SIGQUIT */

zlog(ZLOG_DEBUG, "received SIGQUIT");

zlog(ZLOG_NOTICE, "Finishing ...");

fpm_pctl(FPM_PCTL_STATE_FINISHING, FPM_PCTL_ACTION_SET);

break;

case '1' : /* SIGUSR1 */

zlog(ZLOG_DEBUG, "received SIGUSR1");

if (0 == fpm_stdio_open_error_log(1)) {

zlog(ZLOG_NOTICE, "error log file re-opened");

} else {

zlog(ZLOG_ERROR, "unable to re-opened error log file");

}

ret = fpm_log_open(1);

if (ret == 0) {

zlog(ZLOG_NOTICE, "access log file re-opened");

} else if (ret == -1) {

zlog(ZLOG_ERROR, "unable to re-opened access log file");

}

/* else no access log are set */

break;

case '2' : /* SIGUSR2 */

zlog(ZLOG_DEBUG, "received SIGUSR2");

zlog(ZLOG_NOTICE, "Reloading in progress ...");

fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET);

break;

}

if (fpm_globals.is_child) {

break;

}

} while (1);

return;

}

onesignal php,PHP FPM源代码反刍品味之五:信号signal处理相关推荐

  1. PHP FPM源代码反刍品味之三: 多进程模型

    转载地址:https://www.jianshu.com/p/542935a3bfa8 本文开始会涉及写源代码, FPM源代码目录位于PHP源代码目录下的sapi/fpm FPM多进程轮廓: FPM大 ...

  2. php 无限执行,PHP FPM源代码反刍品味之一:无限运行程序

    基础: 无限运行程序. 程序可以简单的分为两种类型: 程序启动后,一段时间后,完成了任务,会主动退出,这里称为有限程序. 程序启动后,会一直运行,不会主动退出,这里称为无限程序. 显然,服务器程序如n ...

  3. php 代码从夫,PHP FPM源代码反刍品味之四:事件处理

    FPM master 进程启动后,会进入函数fpm_event_loop,无限循环. 处理事件. 事件概要 master 进程所做的的事,总的来说就是两类: 一 定时器事件 简称timer事件,需按时 ...

  4. go语言调度器源代码情景分析之五:汇编指令

    本文是<go调度器源代码情景分析>系列 第一章 预备知识的第4小节. 汇编语言是每位后端程序员都应该掌握的一门语言,因为学会了汇编语言,不管是对我们调试程序还是研究与理解计算机底层的一些运 ...

  5. laravel 资源篇

    转自:https://github.com/qianyugang/learn-laravel # Learn-Laravel - 学习资料和开源项目集 ## Laravel 学习资料 ### 官方网站 ...

  6. linux apache fcgi,Apache使用fcgi方式与PHP结合

    简介 FCGI全称Fast Common Gateway Interface(快速通用网关协议),是一种让交互程序与Web服务器通信的协议.FastCGI是早期通用网关接口(CGI)的增强版本,Fas ...

  7. php类型 fcgi,Apache使用fcgi方式与PHP结合

    简介 FCGI全称Fast Common Gateway Interface(快速通用网关协议),是一种让交互程序与Web服务器通信的协议.FastCGI是早期通用网关接口(CGI)的增强版本,Fas ...

  8. PHP 高级工程面试题汇总

    PHP高级工程面试题汇总(2018.05) 1.给你四个坐标点,判断它们能不能组成一个矩形,如判断([0,0],[0,1],[1,1],[1,0])能组成一个矩形. 勾股定理,矩形是对角线相等的四边形 ...

  9. Tomcat源代码阅读系列之八:Tomcat 设计模式总结

    本篇我们将来分析一下Tomcat中所涉及到设计模式,本文我们将主要来分析 外观模式 , 观察者模式 , 责任链模式 , 模板方法模式 , 命令模式 . 在开始本文之前,笔者先说明一下对于设计模式的一点 ...

最新文章

  1. 在Android中使用Handler和Thread线程执行后台操作
  2. https wireshark抓包——要解密出原始数据光有ssl 证书还不行,还要有浏览器内的pre-master-secret(内存里)...
  3. IO多路复用select/poll/epoll详解以及在Python中的应用
  4. EL表达式 jsp2.0 jsp1.2 与 tomcat
  5. 绑定注意事项——数据源的属性
  6. 阿里P8架构师谈:多线程、架构、异步消息、Redis等性能优化策略
  7. 基于点特征的各位姿求解算法对比(pose-estimation-compared)
  8. 多线程访问DataTable
  9. 箭头函数:this的指向问题
  10. java删不了_java – 为什么我不能删除项目?
  11. Java if/else switchswitch
  12. 【网络安全】实操XSS订单系统漏洞(利用盲打)
  13. PHP 的 SAPI 是个什么东西(转)
  14. JAVA毕业设计酒店管理系统设计与实现计算机源码+lw文档+系统+调试部署+数据库
  15. python基础-包文件批量导入导出
  16. 电脑配件 - 如何选择电脑显示屏幕 - 学习/实践
  17. PHP 面试总结(持续更新) --小丑
  18. Nature子刊:AI技术从零开始设计具有生物活性的蛋白质
  19. Controlling GC pauses with the GarbageFirst Collector
  20. html 取消settimeout,Javascript – setTimeout关闭问题

热门文章

  1. UML 面向对象分析与设计
  2. python我想对你说_python学习第3天-----字典、解构
  3. esp8266oled做时钟python_利用esp8266和鸿蒙带的OLED屏做了一个时钟
  4. 表达式前后缀表达形式 [zz]
  5. java的sql的like_[Java教程]SQL like 模糊查询, in
  6. kmeans鸢尾花分类python代码_python实现鸢尾花三种聚类算法(K-means,AGNES,DBScan)
  7. java四神兽_SpringCloud五大神兽之Eureka
  8. windows下统一mysql编码_mysql5.7 windows7编码统一utf-8
  9. 我的世界中国版服务器地图文件在哪,我的世界中国版手机版联机的地图保存在哪 | 手游网游页游攻略大全...
  10. java 拉起服务_技术开发者应该如何构建小团队的微服务方案?