[インデックス 13373] ファイルの概要
このコミットは、Go言語のランタイムにおけるLinux/386アーキテクチャ向けのSigaction
構造体の定義を修正するものです。具体的には、src/pkg/runtime/defs_linux_386.h
ファイル内で定義されているSigaction
構造体のsa_mask
フィールドのデータ型をuint32
からuint64
に変更しています。これは、Linuxカーネルのヘッダーファイルとの整合性を保ち、正しいシグナルハンドリングを保証するために必要な修正です。
コミット
- コミットハッシュ:
a033e367661d2c76f8949822673ffc36a560852a
- Author: Shenghou Ma minux.ma@gmail.com
- Date: Sun Jun 24 01:41:17 2012 +0800
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/a033e367661d2c76f8949822673ffc36a560852a
元コミット内容
runtime: fix struct Sigaction for Linux/386
We need to use kernel headers to generate defs_linux_$GOARCH.h
R=golang-dev, dave, alex.brainman, iant
CC=golang-dev
https://golang.org/cl/6296091
変更の背景
この変更の背景には、GoランタイムがLinux/386システムコールを正しく呼び出すための、Sigaction
構造体の定義における不整合がありました。Goランタイムは、OSの低レベルな機能(シグナルハンドリングなど)と直接やり取りするために、C言語の構造体定義を模倣した独自の定義を持っています。これらの定義は、各OSおよびアーキテクチャ(例: defs_linux_386.h
)ごとに生成されます。
問題は、Linux/386環境において、Goランタイムが内部的に使用していたSigaction
構造体のsa_mask
フィールドのサイズが、実際のLinuxカーネルが期待するサイズと異なっていた点にあります。具体的には、Goの定義ではuint32
として扱われていましたが、LinuxカーネルのSigaction
構造体では、sa_mask
はシグナルセットを表すためにsigset_t
型を使用しており、これが32ビットシステムであっても64ビットのサイズを持つ場合があるため、uint64
として扱う必要がありました。
この不整合があると、Goプログラムがシグナルを正しく処理できない、あるいは予期せぬクラッシュを引き起こす可能性がありました。コミットメッセージにある「We need to use kernel headers to generate defs_linux_$GOARCH.h」という記述は、Goのランタイム定義が、対象OSの実際のカーネルヘッダーファイル(C言語の定義)と一致していることを確認する必要があることを示唆しています。
前提知識の解説
1. シグナルとシグナルハンドリング
Unix系OSにおいて、シグナルはプロセスに対して非同期的に発生するイベントを通知するメカニズムです。例えば、Ctrl+Cを押すとSIGINT
シグナルが、不正なメモリアクセスが発生するとSIGSEGV
シグナルがプロセスに送信されます。プロセスはこれらのシグナルに対して、デフォルトの動作(終了、コアダンプなど)を実行するか、カスタムのシグナルハンドラ関数を登録して特定の処理を行うことができます。
2. sigaction
システムコールとSigaction
構造体
sigaction
は、シグナルハンドラを設定・変更するためのシステムコールです。このシステムコールは、struct sigaction
という構造体を引数として受け取ります。この構造体は、シグナルハンドラ関数へのポインタ、シグナルハンドラの動作を制御するフラグ、そしてシグナルハンドラ実行中にブロックされるシグナル(シグナルマスク)を定義するsa_mask
フィールドなどを含みます。
3. sa_mask
フィールドとsigset_t
sa_mask
フィールドは、シグナルハンドラが実行されている間にブロックされるシグナルのセットを指定します。これにより、ハンドラが再入可能でない場合に、同じシグナルが再度発生して問題を引き起こすのを防ぐことができます。このフィールドの型は通常sigset_t
です。sigset_t
は、システムによってそのサイズが異なりますが、複数のシグナルをビットマスクとして表現するため、32ビットシステムであっても64ビットのサイズを持つことがあります。これは、シグナル番号が32を超える可能性があるためです。
4. GoランタイムとOSの相互作用
Go言語のプログラムは、OSのシステムコールを直接呼び出すことで、ファイルI/O、ネットワーク通信、プロセス管理、シグナルハンドリングなどの低レベルな操作を行います。Goランタイムは、これらのシステムコールを効率的に、かつGoの並行性モデル(goroutineなど)と統合して利用できるように、OS固有の構造体や定数を内部的に定義しています。これらの定義は、src/pkg/runtime/defs_linux_386.h
のように、OSとアーキテクチャの組み合わせごとに存在します。
5. カーネルヘッダーファイル
カーネルヘッダーファイル(例: /usr/include/linux/signal.h
)は、OSのカーネルが提供するシステムコールやデータ構造のC言語での定義を含んでいます。GoランタイムがOSと正しく連携するためには、これらのカーネルヘッダーファイルで定義されている構造体のレイアウトやフィールドのサイズと、Goランタイムが内部的に持つ定義が完全に一致している必要があります。不一致があると、システムコールが正しく機能しない、データが破損する、あるいはプログラムがクラッシュするなどの問題が発生します。
技術的詳細
このコミットの技術的な核心は、Linux/386アーキテクチャにおけるSigaction
構造体のsa_mask
フィールドの正確なサイズを特定し、Goランタイムの定義をそれに合わせることです。
Linuxカーネルでは、sigset_t
型は通常、__kernel_sigset_t
として定義されており、これはunsigned long
の配列として実装されています。32ビットシステム(i386/x86)では、unsigned long
は32ビットですが、シグナルセットは通常64ビット(2つのunsigned long
)を必要とします。これは、Linuxが64個までのシグナルをサポートしているためです。
Goランタイムのsrc/pkg/runtime/defs_linux_386.h
ファイルでは、当初Sigaction
構造体のsa_mask
フィールドがuint32
として定義されていました。しかし、実際のLinux/386カーネルのsigaction
システムコールが期待するsa_mask
のサイズは64ビット(uint64
)でした。この不一致により、Goプログラムがsigaction
システムコールを呼び出す際に、sa_mask
フィールドに渡されるデータが切り詰められたり、誤ったメモリ位置にアクセスしたりする可能性がありました。結果として、シグナルハンドラが正しく設定されず、シグナルがブロックされない、あるいは予期せぬシグナルがハンドラに到達するといった、シグナル処理に関するバグや不安定性につながる恐れがありました。
この修正は、GoランタイムがLinux/386上で安定して動作し、シグナルを正確に処理するために不可欠でした。Goのビルドプロセスでは、各アーキテクチャ向けのdefs_linux_$GOARCH.h
のようなファイルは、実際のカーネルヘッダーから自動生成されるか、手動でカーネルヘッダーと同期される必要があります。このコミットは、その同期が正しく行われていなかった部分を修正したものです。
コアとなるコードの変更箇所
変更はsrc/pkg/runtime/defs_linux_386.h
ファイルの一箇所のみです。
--- a/src/pkg/runtime/defs_linux_386.h
+++ b/src/pkg/runtime/defs_linux_386.h
@@ -132,7 +132,7 @@ struct Sigaction {
void *k_sa_handler;
uint32 sa_flags;
void *sa_restorer;
- uint32 sa_mask;
+ uint64 sa_mask;
};
struct Siginfo {
int32 si_signo;
コアとなるコードの解説
この変更は、Sigaction
構造体内のsa_mask
フィールドのデータ型をuint32
からuint64
に修正しています。
uint32 sa_mask;
(変更前): これは、sa_mask
が32ビットの符号なし整数として扱われることを意味します。uint64 sa_mask;
(変更後): これは、sa_mask
が64ビットの符号なし整数として扱われることを意味します。
この修正により、GoランタイムがLinux/386上でsigaction
システムコールを呼び出す際に、sa_mask
フィールドに渡されるシグナルマスクが、Linuxカーネルが期待する64ビットのサイズと正確に一致するようになります。これにより、シグナルハンドラが正しく登録され、シグナルマスクが意図した通りに機能し、Goプログラムのシグナル処理の信頼性と安定性が向上します。
関連リンク
- Go CL 6296091: https://golang.org/cl/6296091
参考にした情報源リンク
sigaction(2)
man page (Linux): https://man7.org/linux/man-pages/man2/sigaction.2.htmlsigset_t
definition in Linux kernel (example): https://elixir.bootlin.com/linux/latest/source/include/uapi/asm-generic/signal.h#L10- Go runtime source code (for context on
defs_linux_*.h
files): https://github.com/golang/go/tree/master/src/runtime - Understanding
sigset_t
and signal masks: https://www.gnu.org/software/libc/manual/html_node/Blocking-Signals.html