😽

Go言語を使ったOSシグナルの処理

2025/03/08に公開

概要

osos/signalsyscallパッケージ、あとはチャンネルを使えばOSシグナルを処理できるよというお話

コードサンプル

コードとしては以下のような形になる[1]

package main

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

func main() {

	// OSシグナルを受け取るためのチャンネルを作成
	signalChannel := make(chan os.Signal, 1)

	// sigChannelに取り込むOSシグナルの種別を指定
	signal.Notify(signalChannel, syscall.SIGKILL, syscall.SIGTERM)

	for {
		// OSシグナルを受け取ったら処理をする
		sig := <-signalChannel
		fmt.Printf("シグナルを受け取った: %s\n", sig)

		if sig == syscall.SIGTERM {
			fmt.Printf("SIGTERMを受け取ったのでここでグレースフルなシャットダウンのための準備をする\n")
		}
	}
}

signalChannel := make(chan os.Signal, 1)でOSシグナルを受け取るためのチャネルを作成する。

signal.Notify(signalChannel, syscall.SIGKILL, syscall.SIGTERM)でsignalChannelが受け取るOSシグナルを指定する。
ここでは、syscall.SIGKILLを指定しているが、SIGKILLは流石に対応できない模様(後述)

for文の中のsig := <-signalChannelでOSシグナルの取得を待受ける。
OSシグナルを受け取ったら後段の処理に進む。

動作確認

kill コマンドのプロセスIDとすることでSIGTERMシグナルを送ることができる。
送った場合のコマンドの挙動としては次のようになる[2]

$ ./os_signal 
シグナルを受け取った: terminated
SIGTERMを受け取ったのでここでグレースフルなシャットダウンのための準備をする

kill -9 コマンドのプロセスIDSIGKILLシグナルを送ると次のようになる。
(Ubuntu 22.04の場合)

$ ./os_signal
強制終了

シグナルを受け取った処理云々なく、問答無用でプロセスが殺される。

他の処理をしたい場合は?

たとえばWebサーバーを立ち上げつつ、OSシグナルを受け取りたい場合はどうしたら良いか?
goルーチンをシグナルの受取前に立ち上げてその中でwebサーバーを立ち上げる形とするのが現実的な対応。
その際は、丁寧に対応するならSIGTERMを受け取ったタイミングで、Webサーバーをシャットダウンする処理などが必要になる。

脚注
  1. https://github.com/Fufuhu/os_signal にコードそのものは公開している。 ↩︎

  2. go buildして生成されるバイナリでないと意図した動作にはならない。go runはNG ↩︎

Discussion