一、前言
Golang 的系統信號處理主要涉及os包、os.signal包以及syscall包.其中最主要的函數是signal包中的Notify函數:
func Notify(c chan<- os.Signal, sig …os.Signal)
該函數會將進程收到的系統Signal轉發給channel c.轉發哪些信號由該函數的可變參數決定,如果你沒有傳入sig參數,那麼Notify會將系統收到的所有信號轉發給c.如果你像下面這樣調用Notify:
signal.Notify(c, syscall.SIGINT, syscall.SIGUSR1, syscall.SIGUSR2)
則Go只會關注你傳入的Signal類型,其他Signal將會按照默認方式處理,大多都是進程退出.因此你需要在Notify中傳入你要關注和處理的Signal類型,也就是攔截它們,提供自定義處理函數來改變它們的行爲.
二、信號類型
不同平臺的信號定義或許有些不同.下面列出了POSIX中定義的信號. Linux 使用34-64信號用作實時系統中. 命令 man signal 提供了官方的信號介紹. 在POSIX.1-1990標準中定義的信號列表。
第1列爲信號名;
第2列爲對應的信號值,需要注意的是,有些信號名對應着3個信號值,這是因爲這些信號值與平臺相關,將man手冊中對3個信號值的說明摘出如下,the first one is usually valid for alpha and sparc, the middle one for i386, ppc and sh, and the last one for mips.
第3列爲操作系統收到信號後的動作,Term表明默認動作爲終止進程,Ign表明默認動作爲忽略該信號,Core表明默認動作爲終止進程同時輸出core dump,Stop表明默認動作爲停止進程.
第4列爲對信號作用的註釋性說明,淺顯易懂,這裏不再贅述.
需要特別說明的是,SIGKILL和SIGSTOP這兩個信號既不能被應用程序捕獲,也不能被操作系統阻塞或忽略.
信號 | 值 | 動作 | 說明 |
---|---|---|---|
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) |
四、Go中的Signal發送和處理
- golang中對信號的處理主要使用os/signal包中的兩個方法:
- notify方法用來監聽收到的信號
- stop方法用來取消監聽
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
"time"
)
// 優雅退出go守護進程
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("退出", s)
ExitFunc()
case syscall.SIGUSR1:
fmt.Println("usr1", s)
case syscall.SIGUSR2:
fmt.Println("usr2", s)
default:
fmt.Println("other", s)
}
}
}()
fmt.Println("進程啓動...")
sum := 0
for {
sum++
fmt.Println("sum:", sum)
time.Sleep(time.Second)
}
}
func ExitFunc() {
fmt.Println("開始退出...")
fmt.Println("執行清理...")
fmt.Println("結束退出...")
os.Exit(0)
}