takuroooのブログ

勉強したこととか

Go シグナルをチャネルで受け取る

golang.org

signal.Notifyを使うとチャネルでシグナルを受け取ることができる。

package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
)

func main() {
    sigCh := make(chan os.Signal)
    signal.Notify(sigCh, syscall.SIGINT)

    s := <-sigCh

    fmt.Printf("%T %v\n", s, s)
}

Ctrl+cを実行するとSIGINTがプログラムに通知されてsの中に受信したシグナルが代入される。

signal.Notifyを使ってシグナルをプログラムで受信することで、(シグナルを受信した後に何かしらの停止処理を実行することで)プログラムを安全に止めることができる。

シグナル受信用のチャネルの型はos.Signalを使う。os.Signalはインターフェースとして定義されている。

type Signal interface {
    String() string
    Signal() // to distinguish from other Stringers
}

os - The Go Programming Language

通知されるシグナルは全てこのインターフェースを実装している。

通知されるシグナルはsyscallに定義されていて、signal.Notifyで受信したいシグナルを指定するときはここにある定義を使う。

syscall - The Go Programming Language

const (
    SIGABRT   = Signal(0x6)
    SIGALRM   = Signal(0xe)
    SIGBUS    = Signal(0x7)
    SIGCHLD   = Signal(0x11)
    SIGCLD    = Signal(0x11)
    SIGCONT   = Signal(0x12)
    SIGFPE    = Signal(0x8)
    SIGHUP    = Signal(0x1)
    SIGILL    = Signal(0x4)
    SIGINT    = Signal(0x2)
    ・
    ・
    ・

↑ 定義を見るとSignal型でキャストされて整数を設定している。ここのSignalos.Signalではなく、syscall.Signalである。

syscallで定義されているSignalは以下のようになっている。

type Signal int

これに加えて、os.Signalインターフェースを実装している。なので先にあったように受信する型をos.Signalに指定すればsyscall.Signalをそのまま受け取ることができる。