[golang]golang signal.Notify 信號,如何優雅的退出
golang 中的signal 包的Notify函數
函數聲明為
func Notify(c chan<- os.Signal, sig ...os.Signal)
官方描述:
Notify函數讓signal包將輸入信號轉發到c。如果沒有列出要傳遞的信號,會將所有輸入信號傳遞到c;否則只傳遞列出的輸入信號。
signal包不會為了向c發送信息而阻塞(就是說如果發送時c阻塞了,signal包會直接放棄):調用者應該保證c有足夠的緩存空間可以跟上期望的信號頻率。對使用單一信號用於通知的通道,緩存為1就足夠了。
示例代碼:
ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGSTOP, syscall.SIGUSR1) for { s := <-ch switch s { case syscall.SIGQUIT: log.Infof("SIGSTOP") return case syscall.SIGSTOP: log.Infof("SIGSTOP") return case syscall.SIGHUP: log.Infof("SIGHUP") return case syscall.SIGKILL: log.Infof("SIGKILL") return case syscall.SIGUSR1: log.Infof("SIGUSR1") return default: log.Infof("default") return } }
以上代碼告訴 signal ,將對應的信號通知 ch,然后在 for 循環中針對不同信號做不同的處理, for 循環為死循環。
優雅退出go守護進程
package main import ( "fmt" "os" "os/signal" "syscall" "time" ) func main() { //創建監聽退出chan c := make(chan os.Signal) //監聽指定信號 ctrl+c kill signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGUSR1, syscall.SIGUSR2) go func() { for s := range c { switch s { case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT: fmt.Println("Program Exit...", s) GracefullExit() case syscall.SIGUSR1: fmt.Println("usr1 signal", s) case syscall.SIGUSR2: fmt.Println("usr2 signal", s) default: fmt.Println("other signal", s) } } }() fmt.Println("Program Start...") sum := 0 for { sum++ fmt.Println("sum:", sum) time.Sleep(time.Second) } } func GracefullExit() { fmt.Println("Start Exit...") fmt.Println("Execute Clean...") fmt.Println("End Exit...") os.Exit(0) }
Linux Signal及Golang中的信號處理
信號(Signal)是Linux, 類Unix和其它POSIX兼容的操作系統中用來進程間通訊的一種方式。一個信號就是一個異步的通知,發送給某個進程,或者同進程的某個線程,告訴它們某個事件發生了。
當信號發送到某個進程中時,操作系統會中斷該進程的正常流程,並進入相應的信號處理函數執行操作,完成后再回到中斷的地方繼續執行。
如果目標進程先前注冊了某個信號的處理程序(signal handler),則此處理程序會被調用,否則缺省的處理程序被調用。
https://colobu.com/2015/10/09/Linux-Signals/
信號類型
個平台的信號定義或許有些不同。下面列出了POSIX中定義的信號。
Linux 使用34-64信號用作實時系統中。
命令man 7 signal提供了官方的信號介紹。
在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 | 后台程序向終端中寫數據時觸發 |
在SUSv2和POSIX.1-2001標准中的信號列表:
| 信號 | 值 | 動作 | 說明 |
|---|---|---|---|
| SIGTRAP | 5 | Core | Trap指令觸發(如斷點,在調試器中使用) |
| SIGBUS | 0,7,10 | Core | 非法地址(內存地址對齊錯誤) |
| SIGPOLL | Term | Pollable event (Sys V). Synonym for SIGIO | |
| SIGPROF | 27,27,29 | Term | 性能時鍾信號(包含系統調用時間和進程占用CPU的時間) |
| SIGSYS | 12,31,12 | Core | 無效的系統調用(SVr4) |
| SIGURG | 16,23,21 | Ign | 有緊急數據到達Socket(4.2BSD) |
| SIGVTALRM | 26,26,28 | Term | 虛擬時鍾信號(進程占用CPU的時間)(4.2BSD) |
| SIGXCPU | 24,24,30 | Core | 超過CPU時間資源限制(4.2BSD) |
| SIGXFSZ | 25,25,31 | Core | 超過文件大小資源限制(4.2BSD) |
Windows中沒有SIGUSR1,可以用SIGBREAK或者SIGINT代替。
