Golang訊號處理和優雅退出守護進程

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

Golang中的訊號處理

  • 訊號類型
個平台的訊號定義或許有些不同。下面列出了POSIX中定義的訊號。Linux 使用34-64訊號用作即時系統中。命令 man 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)
第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這兩個訊號既不能被應用程式捕獲,也不能被作業系統阻塞或忽略。
  • kill pid與kill -9 pid的區別
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系統產生什麼不利影響。
  • 應用程式如何優雅退出
Linux Server端的應用程式經常會長時間運行,在運行過程中,可能申請了很多系統資源,也可能儲存了很多狀態,在這些情境下,我們希望進程在退出前,可以釋放資源或將目前狀態dump到磁碟上或列印一些重要的日誌,也就是希望進程優雅退出(exit gracefully)。
從上面的介紹不難看出,優雅退出可以通過捕獲SIGTERM來實現。具體來講,通常只需要兩步動作:1)註冊SIGTERM訊號的處理函數並在處理函數中做一些進程退出的準備。訊號處理函數的註冊可以通過signal()或sigaction()來實現,其中,推薦使用後者來實現訊號響應函數的設定。訊號處理函數的邏輯越簡單越好,通常的做法是在該函數中設定一個bool型的flag變數以表明進程收到了SIGTERM訊號,準備退出。2)在主進程的main()中,通過類似於while(!bQuit)的邏輯來檢測那個flag變數,一旦bQuit在signal handler function中被置為true,則主進程退出while()迴圈,接下來就是一些釋放資源或dump進程目前狀態或記錄日誌的動作,完成這些後,主進程退出。

Go中的Signal發送和處理

  • golang中對訊號的處理主要使用os/signal包中的兩個方法:
  • notify方法用來監聽收到的訊號
  • stop方法用來取消監聽

1.監聽全部訊號

package mainimport (    "fmt"    "os"    "os/signal")// 監聽全部訊號func main()  {    //合建chan    c := make(chan os.Signal)    //監聽所有訊號    signal.Notify(c)    //阻塞直到有訊號傳入    fmt.Println("啟動")    s := <-c    fmt.Println("退出訊號", s)}
啟動go run example-1.go啟動ctrl+c退出,輸出退出訊號 interruptkill pid 輸出退出訊號 terminated

2.監聽指定訊號

package mainimport (    "fmt"    "os"    "os/signal"    "syscall")// 監聽指定訊號func main()  {    //合建chan    c := make(chan os.Signal)    //監聽指定訊號 ctrl+c kill    signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGUSR1, syscall.SIGUSR2)    //阻塞直到有訊號傳入    fmt.Println("啟動")    //阻塞直至有訊號傳入    s := <-c    fmt.Println("退出訊號", s)}
啟動go run example-2.go啟動ctrl+c退出,輸出退出訊號 interruptkill pid 輸出退出訊號 terminatedkill -USR1 pid 輸出退出訊號 user defined signal 1kill -USR2 pid 輸出退出訊號 user defined signal 2

3.優雅退出go守護進程

package mainimport (    "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)}
kill -USR1 pid 輸出usr1 user defined signal 1kill -USR2 pid usr2 user defined signal 2kill pid 退出 terminated開始退出...執行清理...結束退出...
執行輸出go run example-3.go進程啟動...sum: 1sum: 2sum: 3sum: 4sum: 5sum: 6sum: 7sum: 8sum: 9usr1 user defined signal 1sum: 10sum: 11sum: 12sum: 13sum: 14usr2 user defined signal 2sum: 15sum: 16sum: 17退出 terminated開始退出...執行清理...結束退出...

參考

http://www.cnblogs.com/jkkkk/p/6180016.htmlhttp://blog.csdn.net/zzhongcy/article/details/50601079https://www.douban.com/note/484935836/https://gist.github.com/reiki4040/be3705f307d3cd136e85#file-signal-go-L1
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.