Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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プログラムのシグナル処理の信頼性と安定性が向上します。

関連リンク

参考にした情報源リンク