Go中的系統Signal處理

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

我們在生產環境下啟動並執行系統要求優雅退出,即程式接收退出通知後,會有機會先執行一段清理代碼,將收尾工作做完後再真正退出。我們採用系統Signal來 通知系統退出,即kill pragram-pid。我們在程式中針對一些系統訊號設定了處理函數,當收到訊號後,會執行相關清理程式或通知各個子進程做自清理。kill -9強制殺掉程式是不能被接受的,那樣會導致某些處理過程被強制中斷,留下無法恢複的現場,導致訊息被破壞,影響下次系統啟動運行。

最近用Golang實現的一個代理程式也需要優雅退出,因此我嘗試瞭解了一下Golang中對系統Signal的處理方式,這裡和大家分享。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類型,也就是攔截它們,提供自訂處理函數來改變它們的行為。

下面是一個較為完整的例子:

//signal.go

package main

import "fmt"
import "time"
import "os"
import "os/signal"
import "syscall"

type signalHandler func(s os.Signal, arg interface{})

type signalSet struct {
    m map[os.Signal]signalHandler
}

func signalSetNew()(*signalSet){
    ss := new(signalSet)
    ss.m = make(map[os.Signal]signalHandler)
    return ss
}

func (set *signalSet) register(s os.Signal, handler signalHandler) {
    if _, found := set.m[s]; !found {
        set.m[s] =  handler
    }
}

func (set *signalSet) handle(sig os.Signal, arg interface{})(err error) {
    if _, found := set.m[sig]; found {
        set.m[sig](sig, arg)
        return nil
    } else {
        return fmt.Errorf("No handler available for signal %v", sig)
    }

    panic("won't reach here")
}

func main() {
    go sysSignalHandleDemo()
    time.Sleep(time.Hour) // make the main goroutine wait!
}

func sysSignalHandleDemo() {
    ss := signalSetNew()
    handler := func(s os.Signal, arg interface{}) {
        fmt.Printf("handle signal: %v\n", s)
    }

    ss.register(syscall.SIGINT, handler)
    ss.register(syscall.SIGUSR1, handler)
    ss.register(syscall.SIGUSR2, handler)

    for {
        c := make(chan os.Signal)
        var sigs []os.Signal
        for sig := range ss.m {
            sigs = append(sigs, sig)
        }
        signal.Notify(c)
        sig := <-c

        err := ss.handle(sig, nil)
        if (err != nil) {
            fmt.Printf("unknown signal received: %v\n", sig)
            os.Exit(1)
        }
    }
}

上例中Notify函數只有一個參數,沒有傳入要關注的sig,因此程式會將收到的所有類型Signal都轉寄到channel c中。build該源檔案並執行程式:

$> go build signal.go
$> signal

在另外一個視窗下執行如下命令:
$> ps -ef|grep signal
tonybai  25271  1087  0 16:27 pts/1    00:00:00 signal
$> kill -n 2 25271
$> kill -n 12 25271
$> kill 25271

我們在第一個視窗會看到如下輸出:
$> signal
handle signal: interrupt
handle signal: user defined signal 2
unknown signal received: terminated

在sysSignalHandleDemo中我們也可以為Notify傳入我們所關注的Signal集合:

signal.Notify(c, sigs…)

這樣只有在該集合中的訊號我們才能捕獲,收到未在集合中的訊號時,程式多直接退出。上面只是一個Demo,只是說明了我們可以捕捉到我們所關注的訊號,並未體現程式如何優雅退出,不同程式的退出方式不同,這裡沒有通用方法,就不細說了,你的程式需要你專門的設計。

另外我們生產環境下的程式多是以Daemon守護進程的形式啟動並執行。我們用C實現的程式多參考“Unix進階編程”中的方法將程式轉為Daemon Process,但在Go中目前尚提供相關方式,網上有一些實現,但據說都不理想。更多的Go開發人員建議不要在代碼中實現Daemon轉換,建議直接利用 第三方工具。比如在Ubuntu下我們可以使用start-stop-daemon這個小程式輕鬆將你的程式轉換為Daemon:

$> start-stop-daemon –start –pidfile ./signal.pid –startas /home/tonybai/test/go/signal –background -m
$> start-stop-daemon –stop –pidfile ./signal.pid –startas /home/tonybai/test/go/signal

這裡注意:只有加上-m選項,pidfile才能成功建立。

start-stop-daemon在Debian系的Linux發行版中都是預設內建的。但在Redhat系Linux發行版中卻沒有該工具,我們可以自行安裝:

wget -c http://developer.axis.com/download/distribution/apps-sys-utils-start-stop-daemon-IR1_9_18-2.tar.gz
tar -xzf apps-sys-utils-start-stop-daemon-IR1_9_18-2.tar.gz
cd apps/sys-utils/start-stop-daemon-IR1_9_18-2
gcc start-stop-daemon.c -o start-stop-daemon

切換到root下
cp start-stop-daemon /sbin/
chmod +x /sbin/start-stop-daemon

另外Go 1.0.2提供的二進位安裝包直接在Redhat 5.6(Linux tonybai 2.6.18-238.el5 #1 SMP Sun Dec 19 14:22:44 EST 2010 x86_64 x86_64 x86_64 GNU/Linux)下面運行出錯,提示無法找到GLIBC 2.7版本。目前解決這一問題的方法似乎只有從源碼編譯安裝。進入到$GOROOT/src下,執行./all.bash即可。重現編譯連結後的go可執 行程式則運行一切正常。

2012, bigwhite. 著作權.

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.