天天看點

go語言中signal.Notify

go中的信号量

信号 動作 說明
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 背景程式向終端中寫資料時觸發

有些信号名對應着3個信号值,這是因為這些信号值與平台相關

SIGKILL和SIGSTOP這兩個信号既不能被應用程式捕獲,也不能被作業系統阻塞或忽略

kill與kill9的差別

  • kill pid的作用是向程序号為pid的程序發送SIGTERM(這是kill預設發送的信号),該信号是一個結束程序的信号且可以被應用程式捕獲。若應用程式沒有捕獲并響應該信号的邏輯代碼,則該信号的預設動作是kill掉程序。這是終止指定程序的推薦做法。
  • kill -9 pid則是向程序号為pid的程序發送SIGKILL(該信号的編号為9),從本文上面的說明可知,SIGKILL既不能被應用程式捕獲,也不能被阻塞或忽略,其動作是立即結束指定程序。通俗地說,應用程式根本無法“感覺”SIGKILL信号,它在完全無準備的情況下,就被收到SIGKILL信号的作業系統給幹掉了,顯然,在這種“暴力”情況下,應用程式完全沒有釋放目前占用資源的機會。事實上,SIGKILL信号是直接發給init程序的,它收到該信号後,負責終止pid指定的程序。在某些情況下(如程序已經hang死,無法響應正常信号),就可以使用kill -9來結束程序。
  • 若通過kill結束的程序是一個建立過子程序的父程序,則其子程序就會成為孤兒程序(Orphan Process),這種情況下,子程序的退出狀态就不能再被應用程序捕獲(因為作為父程序的應用程式已經不存在了),不過應該不會對整個linux系統産生什麼不利影響。

優雅地退出程式

在長時間的程式運作過程中,可能有大量的系統資源被申請,無論在以何種方式退出前,他們應該及時将這些資源釋放并将狀态輸出到日志中友善調試和排錯。

signal.Notify方法監聽和捕獲信号量

func Notify(c chan<- os.Signal, sig …os.Signal)

首先定義一個chan傳遞信号量,然後說明那些信号量是需要被捕獲的(不填的話就預設捕獲任何信号量)

sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
           

監聽指定信号量

EXIT:
	for {
		sig := <-sc
		switch sig {
		case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
			log.Panic("SIGQUIT")
		case syscall.SIGHUP:
			log.Panic("SIGHUP")
		case syscall.SIGHUP:
			log.Panic("SIGINT")
		default:
			break EXIT
		}
	}