シグナルを受ける
Goで作ったプログラム実行中にシグナルを受け取れるか試してみる。
これができれば、リモートからの通信や、他のイベントを1つのselectで一気に待機できるかも。
signal.Notify()というメソッドが用意されているので、これで試してみる。
godocを見ると、1つめの引数のチャネルに、登録したいシグナルを指定していくみたい。
// Notify causes package signal to relay incoming signals to c.
// If no signals are provided, all incoming signals will be relayed to c.
// Otherwise, just the provided signals will.
//
// Package signal will not block sending to c: the caller must ensure
// that c has sufficient buffer space to keep up with the expected
// signal rate. For a channel used for notification of just one signal value,
// a buffer of size 1 is sufficient.
//
// It is allowed to call Notify multiple times with the same channel:
// each call expands the set of signals sent to that channel.
// The only way to remove signals from the set is to call Stop.
//
// It is allowed to call Notify multiple times with different channels
// and the same signals: each channel receives copies of incoming
// signals independently.
func Notify(c chan<- os.Signal, sig ...os.Signal) {
しかし、シグナルはどうやって指定するのか、、、、1とか2とかで選ぶのかな?
SIGTERMでGoの中をgrepすると、それっぽいファイルを発見。
./syscall/zerrors_darwin_amd64.go
当方マカーなのでdarwinがついているけど、windowsもあるみたい。
中をのぞいてみると、それっぽい。
// Signals
const (
SIGABRT = Signal(0x6)
SIGALRM = Signal(0xe)
SIGBUS = Signal(0xa)
SIGCHLD = Signal(0x14)
SIGCONT = Signal(0x13)
SIGEMT = Signal(0x7)
SIGFPE = Signal(0x8)
SIGHUP = Signal(0x1)
SIGILL = Signal(0x4)
SIGINFO = Signal(0x1d)
SIGINT = Signal(0x2)
SIGIO = Signal(0x17)
SIGIOT = Signal(0x6)
SIGKILL = Signal(0x9)
SIGPIPE = Signal(0xd)
SIGPROF = Signal(0x1b)
SIGQUIT = Signal(0x3)
SIGSEGV = Signal(0xb)
SIGSTOP = Signal(0x11)
SIGSYS = Signal(0xc)
SIGTERM = Signal(0xf)
SIGTRAP = Signal(0x5)
SIGTSTP = Signal(0x12)
SIGTTIN = Signal(0x15)
SIGTTOU = Signal(0x16)
SIGURG = Signal(0x10)
SIGUSR1 = Signal(0x1e)
SIGUSR2 = Signal(0x1f)
SIGVTALRM = Signal(0x1a)
SIGWINCH = Signal(0x1c)
SIGXCPU = Signal(0x18)
SIGXFSZ = Signal(0x19)
)
とりあえずこれでやってみる。
package main
import (
"fmt"
"os"
"syscall"
"os/signal"
)
func main() {
signalCh := make(chan os.Signal)
// SIGHUPとSIGTERMとSIGKILLに対応する
signal.Notify(signalCh, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGKILL)
for {
select {
case ch := <-signalCh:
catchSig(ch)
}
}
}
func catchSig(sig os.Signal) {
switch sig {
case syscall.SIGHUP:
fmt.Println("SIGHUP Happend! ", sig)
case syscall.SIGTERM:
fmt.Println("SIGTERM Happend! ", sig)
case syscall.SIGKILL:
fmt.Println("SIGKILL Happend! ", sig)
default:
fmt.Println("Other singal Happend! ", sig)
}
}
これで動かすと。。。
$./samples &
[2] 34210
$
$ kill 34210
SIGTERM Happend! terminated
$ kill -1 34210
SIGHUP Happend! hangup
$ kill -9 34212
[2]+ Killed: 9 ./samples
さすがにSIGKILLは受け取れなかったけど、狙い通り。
一応未登録のシグナルも、発行してみる。
$ ./samples &
[2] 34226
$ kill -30 34226
やはり何も起こらない。SIGINTも発行する。
$ kill -2 34226
[2]+ Exit 2 ./samples
プログラムは何も受け取らないが、標準動作で終了する。
予期しないシグナルを防ぐには、何もしないように防ぐための登録が必要かも。
Windowsだとどうなる?
Windows版にも、ztypes_windows.goがあって、そこに定義があった。
const (
// More invented values for signals
SIGHUP = Signal(0x1)
SIGINT = Signal(0x2)
SIGQUIT = Signal(0x3)
SIGILL = Signal(0x4)
SIGTRAP = Signal(0x5)
SIGABRT = Signal(0x6)
SIGBUS = Signal(0x7)
SIGFPE = Signal(0x8)
SIGKILL = Signal(0x9)
SIGSEGV = Signal(0xb)
SIGPIPE = Signal(0xd)
SIGALRM = Signal(0xe)
SIGTERM = Signal(0xf)
)
ここにあるシグナルは、受け取れるってことなのかな?
今度試してみよう。