[インデックス 15275] ファイルの概要
このコミットは、GoランタイムがLinuxシステムコールrt_sigaction
を使用する際に、その戻り値をチェックする変更を導入しています。具体的には、rt_sigaction
呼び出しが失敗した場合(例えば、シグナルマスクのサイズが不正な場合など)に、ランタイムがパニックを起こすように修正されています。これにより、シグナルハンドリングの設定における潜在的な問題を早期に検出し、デバッグを容易にすることが目的です。
コミット
commit d3d89ae7d21d8253626aebf212a63d670561f659
Author: Russ Cox <rsc@golang.org>
Date: Fri Feb 15 13:13:19 2013 -0500
runtime: check rt_sigaction return values on linux
(If the mask size is wrong the system call fails.)
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/7305097
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d3d89ae7d21d8253626aebf212a63d670561f659
元コミット内容
このコミットの元のメッセージは以下の通りです。
runtime: check rt_sigaction return values on linux
(If the mask size is wrong the system call fails.)
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/7305097
これは、Linux上でのrt_sigaction
システムコールの戻り値を確認する変更であり、特にシグナルマスクのサイズが不正な場合にシステムコールが失敗する可能性があることを示唆しています。
変更の背景
Goランタイムは、プログラムの実行環境を管理し、ガベージコレクション、スケジューリング、そしてシグナルハンドリングといった低レベルの操作を処理します。シグナルハンドリングは、オペレーティングシステムからのイベント(例えば、プログラムの終了要求、セグメンテーション違反など)をGoプログラムがどのように処理するかを決定する重要な部分です。
Linuxシステムでは、シグナルハンドリングの設定にはsigaction
またはrt_sigaction
システムコールが使用されます。これらのシステムコールは、シグナルハンドラの設定、シグナルマスクの変更、およびその他のシグナル関連の動作を制御します。通常、システムコールは成功した場合は0を返し、失敗した場合は-1を返し、errno
にエラーコードを設定します。
このコミット以前のGoランタイムでは、rt_sigaction
システムコールの戻り値が適切にチェックされていませんでした。これは、システムコールが何らかの理由で失敗した場合(例えば、不正な引数やリソース不足など)、Goランタイムはその失敗を検知できず、予期せぬ動作やクラッシュにつながる可能性がありました。特に、コミットメッセージで言及されているように「マスクサイズが間違っているとシステムコールが失敗する」というケースは、Goランタイムが内部的にsigaction
構造体のサイズを誤って計算したり、カーネルとのABI(Application Binary Interface)の不一致があったりする場合に発生しうる問題です。
この変更の背景には、Goランタイムの堅牢性を向上させ、シグナルハンドリングのセットアップにおける潜在的なエラーを早期に捕捉するという目的があります。エラーを検知せずに処理を続行するのではなく、即座にパニックを発生させることで、開発者が問題を特定しやすくなります。
前提知識の解説
1. シグナル (Signals)
シグナルは、Unix系オペレーティングシステムにおいて、プロセスに対して非同期的にイベントを通知するメカニズムです。例えば、ユーザーがCtrl+Cを押すとSIGINT
シグナルが、プログラムが不正なメモリアクセスをするとSIGSEGV
シグナルが、端末が切断されるとSIGHUP
シグナルが送信されます。プロセスはこれらのシグナルを受信し、デフォルトの動作を実行するか、カスタムのシグナルハンドラを呼び出すことができます。
2. rt_sigaction
システムコール
rt_sigaction
は、Linuxカーネルが提供するシステムコールの一つで、シグナルハンドラを設定するために使用されます。これは古いsigaction
システムコールの拡張版であり、より大きなシグナルセット(シグナルマスク)を扱うことができます。
rt_sigaction
の基本的なプロトタイプは以下のようになります(C言語の観点から):
int rt_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact, size_t sigsetsize);
signum
: 設定するシグナルの番号(例:SIGHUP
,SIGINT
)。act
: 新しいシグナルハンドラの設定を含むsigaction
構造体へのポインタ。oldact
: 以前のシグナルハンドラの設定を保存するためのsigaction
構造体へのポインタ(不要な場合はNULL
)。sigsetsize
:sigaction
構造体内のシグナルマスク(sa_mask
)のサイズをバイト単位で指定します。これは通常、sizeof(sigset_t)
に設定されます。この値がカーネルの期待する値と異なる場合、システムコールは失敗します。
システムコールは成功すると0を返し、エラーが発生すると-1を返します。
3. SIGHUP
シグナル
SIGHUP
(Signal Hang Up) は、通常、制御端末が切断されたときにプロセスに送信されるシグナルです。デーモンプロセスなどでは、設定ファイルを再読み込みするためにこのシグナルを利用することがよくあります。このコミットでは、SIGHUP
シグナルに対するデフォルトの動作(通常はプロセス終了)をチェックし、もしSIG_IGN
(シグナルを無視する)に設定されている場合は、Goランタイムが特別な処理を行わないようにしています。
4. SIG_IGN
SIG_IGN
は、シグナルハンドラとして設定できる特殊な値で、対応するシグナルを無視するようにプロセスに指示します。
5. runtime·throw
Goランタイム内部で使用される関数で、致命的なエラーが発生した場合にプログラムを終了させます。これはGoのpanic
メカニズムの低レベルな実装の一部であり、回復不可能な状態に陥ったことを示します。
技術的詳細
このコミットは、GoランタイムがLinux上でシグナルハンドリングを設定する際の堅牢性を高めるためのものです。具体的には、src/pkg/runtime/signal_linux_386.c
とsrc/pkg/runtime/signal_linux_arm.c
の2つのファイルに修正が加えられています。これらはそれぞれ、Intel 386アーキテクチャとARMアーキテクチャにおけるLinux固有のシグナルハンドリングロジックを含んでいます。
Goランタイムは、runtime·setsig
関数内で特定のシグナル(この場合はSIGHUP
)の現在のハンドラ設定を読み取るためにrt_sigaction
システムコールを使用しています。このシステムコールは、act
引数にnil
(C言語のNULL
に相当)を渡し、oldact
引数にsigaction
構造体へのポインタを渡すことで、現在のシグナルハンドラ設定を照会するモードで動作します。
変更前は、このrt_sigaction
呼び出しの戻り値がチェックされていませんでした。これは、システムコールが失敗した場合でも、Goランタイムはあたかも成功したかのように処理を続行し、不正なsigaction
構造体の内容に基づいて判断を下す可能性がありました。特に、コミットメッセージが指摘するように、sigsetsize
引数(この場合はsizeof(sa.sa_mask)
)がカーネルの期待する値と一致しない場合にシステムコールが失敗することがあります。このような状況は、Goランタイムがコンパイルされた環境と実行されるカーネルのABIに微妙な不一致がある場合に発生しえます。
変更後は、rt_sigaction
の戻り値がチェックされ、0以外(つまりエラー)の場合にはruntime·throw("rt_sigaction read failure")
が呼び出されるようになりました。これにより、シグナルハンドリングの初期設定段階で問題が発生した場合に、Goプログラムが即座にパニックを起こし、開発者が問題を診断できるようになります。これは、サイレントな失敗を防ぎ、システムの安定性とデバッグ可能性を向上させるための重要な変更です。
この修正は、特にnohup
コマンドの下で実行されるプログラムのSIGHUP
シグナル処理に関連しています。nohup
は、端末が切断されてもプロセスが実行を継続できるようにするコマンドです。Goランタイムは、SIGHUP
がSIG_IGN
に設定されているかどうかを確認することで、nohup
のような環境下での挙動を適切に処理しようとします。このチェックの前にrt_sigaction
が失敗すると、誤った情報に基づいて処理が進む可能性があったため、戻り値のチェックはより重要になります。
コアとなるコードの変更箇所
変更は以下の2つのファイルで行われています。
src/pkg/runtime/signal_linux_386.c
src/pkg/runtime/signal_linux_arm.c
それぞれのファイルで、runtime·setsig
関数内のSIGHUP
シグナルに関する処理部分が修正されています。
変更前:
// src/pkg/runtime/signal_linux_386.c (同様の変更がARM版にも適用)
if(i == SIGHUP) {
runtime·memclr((byte*)&sa, sizeof sa);
runtime·rt_sigaction(i, nil, &sa, sizeof(sa.sa_mask)); // ここで戻り値がチェックされていない
if(sa.k_sa_handler == SIG_IGN)
return;
}
変更後:
// src/pkg/runtime/signal_linux_386.c (同様の変更がARM版にも適用)
if(i == SIGHUP) {
runtime·memclr((byte*)&sa, sizeof sa);
if(runtime·rt_sigaction(i, nil, &sa, sizeof(sa.sa_mask)) != 0) // 戻り値のチェックを追加
runtime·throw("rt_sigaction read failure"); // エラー時にパニック
if(sa.k_sa_handler == SIG_IGN)
return;
}
コアとなるコードの解説
変更の核心は、runtime·rt_sigaction
システムコールの呼び出しをif
文で囲み、その戻り値を評価する点にあります。
-
runtime·rt_sigaction(i, nil, &sa, sizeof(sa.sa_mask))
:i
: シグナル番号(この場合はSIGHUP
)。nil
: 新しいシグナルハンドラを設定しないことを示す(現在の設定を照会するため)。&sa
: 現在のシグナルハンドラ設定を格納するためのsigaction
構造体sa
へのポインタ。sizeof(sa.sa_mask)
: シグナルマスクのサイズ。これはsigset_t
のサイズに相当します。
-
!= 0
:rt_sigaction
システムコールは成功した場合に0を返します。したがって、0以外が返された場合はエラーが発生したことを意味します。 -
runtime·throw("rt_sigaction read failure")
:rt_sigaction
呼び出しが失敗した場合に、Goランタイムがこの関数を呼び出してプログラムを終了させます。引数の文字列は、エラーメッセージとして表示され、問題の原因を特定するのに役立ちます。
この変更により、Goランタイムはシグナルハンドリングの初期設定時に発生する可能性のある低レベルのエラーを検出し、それらをサイレントに無視するのではなく、明示的に報告するようになりました。これにより、Goプログラムの信頼性が向上し、デバッグが容易になります。
関連リンク
- Go Issue: https://golang.org/cl/7305097 (これはGoのコードレビューシステムGerritのリンクであり、コミットメッセージに記載されています)
sigaction(2)
man page: https://man7.org/linux/man-pages/man2/sigaction.2.html (Linuxのsigaction
システムコールに関する公式ドキュメント)
参考にした情報源リンク
- 上記の
sigaction(2)
man page - Go言語のソースコード(特に
src/pkg/runtime
ディレクトリ内の関連ファイル) - Unix/Linuxシグナルハンドリングに関する一般的な知識
nohup
コマンドの動作に関する知識- Go言語の
panic
メカニズムに関する知識 - Go言語のコミット履歴とコードレビューシステム (Gerrit) の利用方法
rt_sigaction
システムコールに関する情報 (Web検索)