Golang 中信号(signal)处理

Go信号通知机制可以通过往一个channel中发送 os.Signal 实现。 使用 signal.Notify 注册要接收的信号。signal.Stop 取消监听。

使用示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func main() {
    //TODO do-somethings
    // block forever
    signals()
}

func signals() {
    // Go signal notification works by sending `os.Signal`
    // values on a channel. We'll create a channel to
    // receive these notifications (we'll also make one to
    // notify us when the program can exit).
    sigs := make(chan os.Signal, 1)
    done := make(chan bool, 1)

    // `signal.Notify` registers the given channel to
    // receive notifications of the specified signals.
    signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

    // This goroutine executes a blocking receive for
    // signals. When it gets one it'll print it out
    // and then notify the program that it can finish.
    go func() {
        sig := <-sigs
        println(sig)
        done <- true
    }()

    // The program will wait here until it gets the
    // expected signal (as indicated by the goroutine
    // above sending a value on `done`) and then exit.
    println("awaiting signal")
    <-done
    println("exiting")
}

信号类型

各个系统平台信号定义可能不完全相同,在POSIX.1-1990标准中定义的信号列表

信号 动作 说明
SIGHUP 1 Term 终端控制进程结束(终端连接断开)
SIGINT 2 Term 用户发送INTR字符(Ctrl+C)触发
SIGQUIT 3 Core 用户发送QUIT字符(Ctrl+/)触发
SIGILL 4 Core 非法指令(程序错误、试图执行数据段、栈溢出等)
SIGABRT 6 Core 调用abort函数触发
SIGFPE 8 Core 算术运行错误(浮点运算错误、除数为零等)
SIGKILL 9 Term 无条件结束程序(不能被捕获、阻塞或忽略)
SIGSEGV 11 Core 无效内存引用(试图访问不属于自己的内存空间、对只读内存空间进行写操作)
SIGPIPE 13 Term 消息管道损坏(FIFO/Socket通信时,管道未打开而进行写操作)
SIGALRM 14 Term 时钟定时信号
SIGTERM 15 Term 结束程序(可以被捕获、阻塞或忽略)
SIGUSR1 30,10,16 Term 用户保留
SIGUSR2 31,12,17 Term 用户保留
SIGCHLD 20,17,18 Ign 子进程结束(由父进程接收)
SIGCONT 19,18,25 Cont 继续执行已经停止的进程(不能被阻塞)
SIGSTOP 17,19,23 Stop 停止进程(不能被捕获、阻塞或忽略)
SIGTSTP 18,20,24 Stop 停止进程(可以被捕获、阻塞或忽略)
SIGTTIN 21,21,26 Stop 后台程序从终端中读取数据时触发
SIGTTOU 22,22,27 Stop 后台程序向终端中写数据时触发

信号动作详解:

  • Term表明默认动作为终止进程
  • Ign表明默认动作为忽略该信号
  • Core表明默认动作为终止进程同时输出core dump
  • Stop表明默认动作为停止进程。

注意:SIGKILLSIGSTOP这两个信号不会被应用程序捕获,也不会被操作系统阻塞或忽略。

比如:

  • kill [pid]命令会发送SIGTERM信号给进程pid,进程可以监听这个信号
  • kill -9 [pid]命令会发送SIGKILL信号,该信号会直接发给init进程,被操作系统接收处理,应用程序无法监听这个信号

darwin amd64操作系统下信号

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Signal table
var signals = [...]string{
    1:  "hangup",
    2:  "interrupt",
    3:  "quit",
    4:  "illegal instruction",
    5:  "trace/BPT trap",
    6:  "abort trap",
    7:  "EMT trap",
    8:  "floating point exception",
    9:  "killed",
    10: "bus error",
    11: "segmentation fault",
    12: "bad system call",
    13: "broken pipe",
    14: "alarm clock",
    15: "terminated",
    16: "urgent I/O condition",
    17: "suspended (signal)",
    18: "suspended",
    19: "continued",
    20: "child exited",
    21: "stopped (tty input)",
    22: "stopped (tty output)",
    23: "I/O possible",
    24: "cputime limit exceeded",
    25: "filesize limit exceeded",
    26: "virtual timer expired",
    27: "profiling timer expired",
    28: "window size changes",
    29: "information request",
    30: "user defined signal 1",
    31: "user defined signal 2",
}

常见输出方式:

  • ctrl+c 输出 interrupt
  • kill [pid] 输出 terminated
  • kill -USR1 [pid] 输出 usr defined signal 1
  • kill -USR2 [pid] 输出 usr defined signal 2

Nginx信号设计机制

  • TERM/INT 快速退出,当前的请求不执⾏完成就退出
  • QUIT 优雅退出,执⾏完当前的请求后退出
  • HUP 重新加载配置⽂件,⽤新的配置⽂件启动新worker进程,并优雅的关闭旧的worker进程
  • USR1 重新打开⽇志⽂件
  • USR2 平滑的升级nginx⼆进制⽂件

参考