😎
(Swift)DispatchSourceを使ってUNIXシグナルを受信する
はじめに
以下の環境で動作確認しました。
- macOS 12.1 Monterey
- Swift 5.5.1
実装例
DispatchSourceを使ってUNIXシグナルを受信する実装例を示します。以下はSIGINTを受信して「Received SIGINT」と表示するプログラムです。
なお、この記事ではsigaction関数とsignal関数を使う2パターンを掲載します。どのような違いがあり、どちらを使うべきかについては各自で調べてください。
sigaction関数を使う実装例
import Foundation
func run() {
let dg = DispatchGroup()
// sigaction構造体とsigaction関数の名前の衝突を防ぐため型エイリアスを定義する
typealias SignalAction = sigaction
var ignoreAction = SignalAction(
__sigaction_u: unsafeBitCast(SIG_IGN, to: __sigaction_u.self),
sa_mask: 0,
sa_flags: 0
)
_ = withUnsafePointer(to: &ignoreAction) { ignoreActionPtr in
// SIGINTを受信したときのデフォルトの挙動を無効にしておく
sigaction(SIGINT, ignoreActionPtr, nil)
}
// シグナル受信用のスレッドを作成する
let queue = DispatchQueue.global(qos: .default)
let src = DispatchSource.makeSignalSource(signal: SIGINT, queue: queue)
src.setEventHandler {
print("Received SIGINT")
dg.leave()
}
// シグナルの受信を開始する
src.activate()
// シグナルを受信するまでプログラムが終了しないように待機する
dg.enter()
dg.wait()
print("Done")
}
run()
signal関数を使う実装例
import Foundation
func run() {
let dg = DispatchGroup()
// SIGINTを受信したときのデフォルトの挙動を無効にしておく
signal(SIGINT, SIG_IGN)
// シグナル受信用のスレッドを作成する
let queue = DispatchQueue.global(qos: .default)
let src = DispatchSource.makeSignalSource(signal: SIGINT, queue: queue)
src.setEventHandler {
print("Received SIGINT")
dg.leave()
}
// シグナルの受信を開始する
src.activate()
// シグナルを受信するまでプログラムが終了しないように待機する
dg.enter()
dg.wait()
print("Done")
}
run()
補足事項
makeSignalSource
にメインスレッド(DispatchQueue.main
)を渡すと処理がブロックされるためシグナルが受信できません。そのため上記の実装例ではDispatchQueue.global
でシグナル受信用のスレッドを作成しています。
試運転
上記の実装例をmain.swift
として保存してください。実行すると以下のように表示されます。
キーボードでCtrl-C
を入力するかkill
コマンドでSIGINTを送信してください。
$ swift main.swift
Received SIGINT
Done
Discussion