信号
信号是UNIX、类UNIX,以及其他POSIX兼容的操作系统中进程间通信的一种有限制的方式。
它是一种异步的通知机制,用来提醒进程一个事件(硬件异常、程序执行异常、外部发出信号)已经发生。当一个信号发送给一个进程时,操作系统中断了进程正常的控制流程。此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则执行默认的处理函数。
查看信号
kill -l 查看
~ kill -l
HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE ALRM TERM 16 CHLD CONT STOP
TSTP TTIN TTOU URG XCPU XFSZ VTALRM PROF WINCH POLL 30 SYS
常用信号
命令 | 信号 | 含义 |
---|---|---|
ctrl+c | SIGINT | 当用户键入终端中断字符(如:Ctrl + C)终端驱动程序将发送该信号给前台进程组。该信号默认行为是终止进程 |
ctrl+z | SIGTSTP | 作业控制的停止信号,当用户在键盘输入挂起字符(如:Ctrl+Z)时,将发送该信号给前台进程组,使其停止运行(该信号可以被处理和忽略) |
ctrl+/ | SIGQUIT | 当用户在键盘键入退出字符(如Ctrl+\)时,该信号将发往前台进程组。默认情况下,该信号终止进程,并生成可用于调试的核心转储文件。当进程陷入无限循环或者不在响应,使用该信号很合适 |
SIGTERM | 这是用来终止进程的标准信号,也是kill和killall命令所发送的默认信号。用户经常会使用kil -9显示向进程发送SIGKILL信号,然而这一做法通常是错误的。精心设计的应用程序应当为SIGTERM信号设置处理器程序,以便于其能够预先清理临时文件和释放资源,做到全身而退。发送SIGKILL信号可以杀掉某个进程,从而绕开了SIGTERM的信号处理程序。因此,总是应该首先尝试使用SIGTERM信号来终止进程,而把SIGKILL信号作为最后手段,去对付那些失控的进程 | |
SIGKILL | 此信号为必杀信号,处理器程序无法阻塞、忽略或者捕获,故而总能杀死进程(僵尸进程除外) | |
SIGHUP | 当终端断开时,将发送该信号给终端控制进程。SIGHUP信号还可用于守护进程 |
重启流程
1.替换可执行文件或修改配置文件
2.发送信号量SIGHUP
3.拒绝新连接求情旧进程,并保证正在处理的连接正常
4.启动新的子进程
5.新的子进程开始处理新的请求
6.旧的进程处理完旧连接后正常退出
package main
import (
"context"
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
s := http.Server{
Addr: ":8080",
Handler: nil,
ReadTimeout: 60,
WriteTimeout: 60,
MaxHeaderBytes: 1 << 20,
}
go func() {
if err := s.ListenAndServe(); err != nil && err != http.ErrServerClosed {
fmt.Printf("http.ListenAndServe err:%v", err)
}
}()
//监听信号
quit := make(chan os.Signal)
//监听 SIGINT 和 SIGTERM
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<- quit
fmt.Printf("Server quiting....")
//设置关闭后程序剩余多长时间处理原有的请求
ctx, cancelFunc := context.WithTimeout(context.Background(), 5 * time.Second)
defer cancelFunc()
if err := s.Shutdown(ctx); err != nil {
fmt.Printf("s.Shutdown(ctx) err:%v", err)
}
fmt.Printf("Server Shutdown....")
}
上面启动后,按ctrl+c可看到执行结果