signal信号的介绍:
https://blog.csdn.net/m0_50502677/article/details/136711385
优雅退出—捕捉signal信号并处理
1.shell命令捕捉signal信号
trap命令
https://www.cnblogs.com/liuhedong/p/10663842.html
// 举例
trap "bash xxx.sh" INT TERM
2.C语言调用signal函数捕捉signal信号
函数原型:
函数原型:
#include <signal.h>
void signal(int signum, void (*handler)(int));
参数:
@signum: 要设置处理函数的信号编号
@handler:指向处理函数的指针。可以是一个自定义的函数,也可以是系统提供的预定义处理函数。
返回值:
无返回值
注意:
(1)关于参数函数指针的参数:
调用 signal() 函数将自定义信号 MY_SIGNAL 与信号处理函数 signal_handler 关联起来。当进程收到信号 MY_SIGNAL 时,操作系统会调用 signal_handler 函数,并将收到的信号编号作为参数传递给它。
(2)关于信号的接收调用:
信号处理函数是由操作系统调用的,而不是直接调用的。当进程收到指定的信号时,操作系统会检查信号处理函数,如果已经注册了与该信号对应的处理函数,就会调用这个处理函数,并传递信号编号作为参数。
(3)总结:
信号处理函数是由操作系统来调用的,它能够知道有信号发生是因为操作系统调用了它。当信号处理函数被调用时,它就知道有信号发生了,并且能够通过参数来获取信号的编号,从而知道这个信号是给自己的。
3.redis中的优雅退出
redis使用sigaction,
参考:
https://github.com/guodongxiaren/LinuxAPI/wiki/sigaction
// 调用了sigaction函数,定义如下
/* Get and/or set the action for signal SIG. */
extern int sigaction (int __sig, const struct sigaction *__restrict __act,
struct sigaction *__restrict __oact) __THROW;
// 具体的使用是构建这个sigaction结构
struct sigaction act;
/* When the SA_SIGINFO flag is set in sa_flags then sa_sigaction is used.
* Otherwise, sa_handler is used. */
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = sigtermHandler;
sigaction(SIGTERM, &act, NULL);
// 在sigtermHandler里,会打开关闭标识, 在serverCron里会识别这个标识,然后做rdb备份等处理
server.shutdown_asap = 1;
// 在每隔100ms运行的serverCron函数里,检测到这个关闭标识后,会调用prepareForShutdown函数后exit
// 另外,也可以运行shotdown的命令去关闭,同样会直接在命令处理中调用prepareForShutdown函数后exit
/* prepareForShutdown函数步骤:
1.如果有 BGSAVE 正在执行,那么杀死子进程,避免竞争条件
2.同理,杀死正在执行 BGREWRITEAOF 的子进程
3.如果客户端执行的是 SHUTDOWN save ,或者 SAVE 功能被打开,那么执行 SAVE 操作
4.移除 pidfile 文件
5.关闭监听套接字,这样在重启的时候会快一点
调用完prepareForShutdown后,注意不是退出aeMain循环执行aeDeleteEventLoop,而是直接exit
*/
4.brpc服务的优雅退出
服务中调用RunUntilAskedToQuit函数
在brpc中RunUntilAskedToQuit的实现是
while (!IsAskedToQuit()) {
bthread_usleep(1000000L);
}
Stop(0/*not used now*/);
Join();
IsAskedToQuit函数
// 而IsAskedToQuit会调用RegisterQuitSignalOrDie
pthread_once(®ister_quit_signal_once, RegisterQuitSignalOrDie);
并返回s_signal_quit标记的结果
// RegisterQuitSignalOrDie中会捕获SIGINT信号,并去关联一个quit_handler
SignalHandler prev = signal(SIGINT, quit_handler);
// 当FLAGS_graceful_quit_on_sigterm=true标记打开时,会捕获SIGTERM信号
SignalHandler prev = signal(SIGTERM, quit_handler);
// 当FLAGS_graceful_quit_on_sighup=true标记打开时,会捕获SIGHUP信号
SignalHandler prev = signal(SIGHUP, quit_handler);
// quit_handler会去设置
s_signal_quit = true;
Stop函数
// 标记状态
_status = STOPPING;
// 拒绝新的请求
_am->StopAccept(timeout_ms);
Join函数
// 调用Accepter的Join,等待当前请求处理完
_am->Join();
// 清理_session_local_data_pool
// 清理_keytable_pool,需要在Join完之后,保证没有任何bthread在用了
// 清理 tls_key
// 调用_derivative_thread的stop和join
// 修改_status状态
_status = READY;
PS:当服务使用supervisorctl管理时,可以配置stopsignal=INT来指定退出时发送的信号,默认是TERM