博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
PHP FPM源代码反刍品味之五:信号signal处理
阅读量:6211 次
发布时间:2019-06-21

本文共 5272 字,大约阅读时间需要 17 分钟。

hot3.png

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

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

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

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

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

  1. master进程,不是直接处理信号,而是通过socketpair创建一个管道,把信号转换一个字符,写到管道里,master进程事件处理无限循环,读取到这个字符时,调用对应的函数.
    socketpair,通常管道不同进程通信,而这里确是在同一个进程内部通信,左手交右手,感觉多此一举.
    这样做的好处是: 避免信号处理函数与事件处理逻辑同时运行的情况.
    注意worker 进程没有用到这个socketpair管道.
  2. 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.cstatic 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;}

转载于:https://my.oschina.net/u/3532467/blog/1554164

你可能感兴趣的文章
Spring4 基本使用
查看>>
通知协议KVO的用法
查看>>
openwrt页面显示问题修改
查看>>
linux dd命令
查看>>
Java编程思想(2)之一切皆对象
查看>>
元素全屏居中(不变形)
查看>>
Java面试006-优化篇
查看>>
使用HANDLECOLLISIONS的几个场景
查看>>
mysqlbinlog命令使用
查看>>
Algs4-1.1.35模拟投骰子
查看>>
*Algs4-1.5.6quick-union的运行时间-(未解决)
查看>>
Hadoop1 Centos伪分布式部署
查看>>
用JavaScript编写气泡
查看>>
如何使用MySQL Workbench创建数据库存储过程
查看>>
乘法逆元...Orz
查看>>
01-前端初识
查看>>
解决修改sources.list之后update NO_PUBKEY错误
查看>>
0802THINKPHP基础:入口文件、路由、页面跳转、重定向、空模块、空控制器、空方法...
查看>>
Docker技术入门与实战 第二版-学习笔记-8-网络功能network-2-相应配置
查看>>
centos6.x升级glibc-2.17
查看>>