unix进程间通信信号的有趣实践

背景

最近优化了一版程序:用到了golang的优雅退出机制。

程序使用leader/follower分布式高可用模型,所有的请求都会命中leader;

使用etcd的election sdk做选主,需要在节点意外下线的时候,主动去etcd卸任(删除10s租约), 否则已经下线的节点还会被etcd认为是leader。

--

所以在这里,优雅退出是技术刚需。

另外根据[云原生十二要素方法论] 第9条: 快速启动和优雅终止可最大化健壮性 , 也推荐各位遵守实践。
Fast startup and shutdown are advocated for a more robust and resilient system.


粗浅的认知方案: 捕获程序的终止信号, 主动去卸任。

1. 进程间通信信号

进程间通信信号是操作系统提供的信号机制的内容, 它不是我们开发者长陷入其中的网络或者Socket通信。

kill -l 命令可以列出系统支持的所有的进程间通信信号吗, 下面黑体是常见的、可能用到的通信信号

  • SIGHUP (1) : 挂起信号,通常用于在终端断开时通知进程,也用于通知进程重新加载配置文件。
  • SIGINT (2) : 中断信号,通常由用户通过 Ctrl+C 组合键发送,用于中断一个进程。
  • SIGQUIT (3) : 退出信号,通常由用户通过 Ctrl+\ 组合键发送,用于退出一个进程,并生成核心转储。
  • SIGILL (4) : 非法指令信号,表示进程试图执行非法指令。
  • SIGABRT (6) : 中止信号,通常由调用 abort() 函数产生,用于异常终止一个进程。
  • SIGFPE (8) : 浮点异常信号,表示进程发生算术错误,如除以零。
  • SIGKILL (9) : 杀死信号,强制终止进程,无法被捕获或忽略。
  • SIGSEGV (11) : 段错误信号,表示进程试图访问未分配的内存。
  • SIGPIPE (13) : 管道破裂信号,表示进程写入一个没有读取端的管道。
  • SIGALRM (14) : 定时器信号,由 alarm() 函数产生,用于定时通知进程。
  • SIGTERM (15) : 终止信号,通常用于请求进程优雅地终止。
  • SIGUSR1 (10) 和 SIGUSR2 (12) : 用户自定义信号,供用户和应用程序自行定义用途。
  • SIGCHLD (17) : 子进程状态改变信号,当子进程终止或停止时,父进程会收到此信号。
  • SIGCONT (18) : 继续执行信号,用于恢复暂停的进程。
  • SIGSTOP (19) : 暂停信号,停止进程的执行,无法被捕获或忽略。
  • SIGTSTP (20) : 终端停止信号,通常由用户通过 Ctrl+Z 组合键发送,用于暂停进程。
  • SIGTTIN (21) 和 SIGTTOU (22) : 后台进程试图从终端读取或写入数据时发送的信号。

2. GO应用侦听SIGINT、SIGTERM信号实现优雅退出

有进程终止效果的常用信号有三个: SIGINT、 SIGKILL、SIGTERM, 其中SIGKILL(kill -9) 无法被应用捕获或忽略。

一般应用捕获SIGINT(interrupt中断)、SIGTERM(terminate终止)信号就可在终止之前注入行为。

golang提供signal包来监听收到的进程间通信信号。

针对长时间运行的程序,新开协程,持续监听信号,并插入优雅关闭的代码。

c := make(chan os.Signal)
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT)
go func() {
		select  {
			case sig:= <-c: {
				log.Infof("Got %s signal. Aborting...\n", sig)
				eCli.Close()    // 利用 etcd election sdk主动卸任
				os.Exit(1)    
			}
		}
	}()

3. 利用SIGSTOP信号模拟进程阻塞

SIGSTOP:暂停进程, 这意味着进程挂起,直到收到SIGCONT信号(continue)才能继续运行。

Q: 进程暂停是什么状态?

1> .收到SIGSTOP信号, 进程会停止运行, 不会执行任何代码,直到被显式恢复;
2>. 不可忽略,这意味着进程收到信号,无法自行决定如何响应,操作系统会直接暂停它;
3>. 资源保持, 虽然被暂停,但依然保留所有系统资源(内存、文件描述符), 这不像进程终止,会释放所有资源。

image.png


curl => 进程A=> 进程B
1>. 对进程B执行kill -STOP {pid} 暂停进程

2>. time curl A(192.168.8.88/test/timeoutAPI) 输出:

0.01s user 0.01s system 0% cpu 10.168 total
 // 这意味着进程A请求B的超时时间是10s

4. Nginx使用SIGHUP信号实现热更新

上面的STOP信号是纯正的暂停信号,进程无法拒绝;

HangUp信号最初用于终端断开时通知应用进程, 默认行为是终止进程;

由于HUP信号可被应用捕获,在现代应用中,HUP信号常用于通知守护进程重新加载其配置文件。

Nginx master进程重写了HUP信号默认终止进程的逻辑,自定义了处理逻辑,实现了配置的热更新。

image.png

在机器与内master进程直接交互, 不是特别便利;
nginx 0.8版本之后,给出了一系列命令行参数, nginx -s reload这个命令会启动一个nginx前台进程,自动定位master进程,向master进程发送HUP信号。

参考资源

posted @ 2022-02-25 09:52  博客猿马甲哥  阅读(534)  评论(1编辑  收藏  举报