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

[インデックス 15292] ファイルの概要

このコミットは、Go言語のランタイムにおけるFreeBSD環境でのシグナルハンドリングに関するバグ修正を扱っています。具体的には、sigaction構造体内のsa_maskメンバーの定義がFreeBSDのシステムコールと一致していなかった問題を解決し、FreeBSD上でのGoのビルドが正常に行われるようにします。

コミット

commit 556dd0bfbd52876933ef0454ca86f492c618f342
Author: Joel Sing <jsing@google.com>
Date:   Mon Feb 18 03:23:29 2013 +1100

    runtime: fix sigaction struct on freebsd
    
    Fix the sa_mask member of the sigaction struct - on FreeBSD this is
    declared as a sigset_t, which is an array of four unsigned ints.
    Replace the current int64 with Sigset from defs_freebsd_GOARCH, which
    has the correct definition.
    
    Unbreaks the FreeBSD builds.
    
    R=golang-dev, dave, minux.ma
    CC=golang-dev
    https://golang.org/cl/7333047

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/556dd0bfbd52876933ef0454ca86f492c618f342

元コミット内容

Goランタイムにおいて、FreeBSD環境でのsigaction構造体のsa_maskメンバーの定義が誤っていたため、FreeBSDでのビルドが失敗していました。このコミットは、sa_maskの型をint64から、FreeBSDのsigset_tに対応するSigset型(4つのunsigned intの配列)に修正することで、この問題を解決し、FreeBSDでのビルドを正常化します。

変更の背景

Go言語は、様々なオペレーティングシステム(OS)やアーキテクチャをサポートするように設計されています。そのため、各OS固有のシステムコールやデータ構造とのインターフェースを正確に定義する必要があります。このコミットが行われた当時、GoのランタイムがFreeBSD上でシグナルを処理する際に使用するsigaction構造体の定義が、FreeBSDの実際の定義と異なっていました。

具体的には、sigaction構造体内のsa_maskフィールドが、Goのランタイムコードではint64として定義されていましたが、FreeBSDのシステムヘッダではsigset_tとして定義されていました。sigset_tは通常、複数のシグナルをビットマスクとして表現するために使用されるデータ型であり、FreeBSDでは4つのunsigned intの配列として実装されていました。この型不一致が原因で、GoのランタイムがFreeBSDのシグナル処理APIを呼び出す際に、不正なメモリアクセスや予期せぬ動作を引き起こし、結果としてFreeBSD上でのGoのビルドが失敗する、あるいは実行時に問題が発生する可能性がありました。

このコミットは、このようなOS固有のインターフェースの不一致を解消し、GoがFreeBSD環境で安定して動作するための重要な修正です。

前提知識の解説

シグナル (Signal)

シグナルは、Unix系OSにおいてプロセス間通信やイベント通知のために使用されるソフトウェア割り込みの一種です。例えば、Ctrl+Cを押すとSIGINTシグナルがプロセスに送られ、プロセスは通常終了します。プログラムはシグナルを受信した際に、デフォルトの動作を実行するか、カスタムのシグナルハンドラ関数を呼び出すかを設定できます。

sigaction構造体

sigaction構造体は、シグナルハンドリングの動作を定義するために使用されるデータ構造です。sigactionシステムコールを通じて、特定のシグナルに対するハンドラ関数、シグナルハンドラ実行中のシグナルマスク、およびその他のフラグを設定します。主要なメンバーは以下の通りです。

  • sa_handler または sa_sigaction: シグナルを受信した際に呼び出されるハンドラ関数へのポインタ。
  • sa_mask: シグナルハンドラが実行されている間にブロックされる(一時的に無視される)シグナルのセットを指定するシグナルマスク。
  • sa_flags: シグナルハンドリングの動作を制御するフラグ(例: SA_SIGINFOで追加情報を受け取る、SA_RESTARTでシステムコールを再開するなど)。

sigset_t

sigset_tは、シグナルのセット(集合)を表すデータ型です。通常、ビットマスクとして実装され、各ビットが特定のシグナルに対応します。sa_maskメンバーはこのsigset_t型であり、シグナルハンドラ実行中にブロックしたいシグナルを指定するために使用されます。OSやアーキテクチャによってその具体的なサイズや内部構造は異なりますが、複数のシグナルを効率的に管理するためのものです。FreeBSDでは、これが4つのunsigned intの配列として定義されていました。

~0ULL~(uint32)0

これらはビット演算子と型キャストを組み合わせた表現です。

  • ~ (ビット反転): オペランドのすべてのビットを反転させます(0を1に、1を0に)。
  • 0ULL: unsigned long long型の0を意味します。すべてのビットが0の64ビット整数です。~0ULLは、すべてのビットが1の64ビット整数(つまり、64ビットの最大値)を生成します。
  • (uint32)0: unsigned int型の0を意味します。すべてのビットが0の32ビット整数です。~(uint32)0は、すべてのビットが1の32ビット整数(つまり、32ビットの最大値)を生成します。

これらの値は、シグナルマスクを「すべてのシグナルをブロックする」状態に設定するためによく使用されます。

技術的詳細

このコミットの核心は、GoランタイムがFreeBSDのsigactionシステムコールと正しくインターフェースするための型定義の修正です。

Goのランタイムは、C言語で書かれた部分(src/pkg/runtime/以下の.cファイル群)とGo言語で書かれた部分が混在しており、OS固有のシステムコールを呼び出す際には、C言語の構造体定義をGoのランタイムコード内で模倣する必要があります。

FreeBSDのsigaction構造体は、sa_maskメンバーをsigset_t型として定義しています。FreeBSDのsigset_tは、内部的には__bits[4]という4つのunsigned intの配列として実装されています。これは、32ビットシステムでは4 * 32 = 128ビット、64ビットシステムでも同様に128ビットのシグナルマスクを表現できることを意味します。

しかし、Goのランタイムコードでは、このsa_maskが誤ってint64(64ビット整数)として定義されていました。この不一致により、Goのランタイムがsigactionシステムコールを呼び出す際に、sa_maskに設定しようとしたデータがFreeBSDカーネルによって正しく解釈されませんでした。具体的には、int64として~0ULL(すべてのビットが1の64ビット値)を設定しても、FreeBSDが期待する128ビットのsigset_tの構造とは合致せず、シグナルマスクが意図通りに設定されない、あるいはメモリアライメントの問題を引き起こす可能性がありました。

この修正では、Goランタイム内のSigaction構造体のsa_maskメンバーの型をint64からSigsetに変更しています。このSigset型は、defs_freebsd_GOARCH(FreeBSDの特定のアーキテクチャ向けの定義ファイル)で、FreeBSDのsigset_tに対応するように、4つのuint32の配列として定義されています。

さらに、シグナルマスクを初期化する部分も変更されています。以前はsa.sa_mask = ~0ULL;としていましたが、これはint64型にすべてのビットを1に設定するものでした。新しいSigset型は配列であるため、各要素を個別に初期化する必要があります。したがって、sa.sa_mask.__bits[0] = ~(uint32)0;からsa.sa_mask.__bits[3] = ~(uint32)0;まで、4つのuint32要素それぞれにすべてのビットを1に設定する値が代入されるようになりました。これにより、FreeBSDが期待する128ビットのシグナルマスクが正しく設定され、すべてのシグナルがブロックされる状態が実現されます。

この修正により、GoのランタイムがFreeBSDのシグナル処理APIと正しく連携できるようになり、FreeBSD上でのGoのビルドおよび実行時の安定性が向上しました。

コアとなるコードの変更箇所

このコミットは、以下の3つのファイルに同様の変更を加えています。これらはそれぞれFreeBSDの異なるアーキテクチャ(386, amd64, arm)に対応するシグナル処理のC言語ソースファイルです。

  1. src/pkg/runtime/signal_freebsd_386.c
  2. src/pkg/runtime/signal_freebsd_amd64.c
  3. src/pkg/runtime/signal_freebsd_arm.c

各ファイルでの変更は以下の通りです(src/pkg/runtime/signal_freebsd_386.cを例に示します)。

--- a/src/pkg/runtime/signal_freebsd_386.c
+++ b/src/pkg/runtime/signal_freebsd_386.c
@@ -15,7 +15,7 @@ typedef struct sigaction {
  		void    (*__sa_sigaction)(int32, Siginfo*, void *);
  	} __sigaction_u;		/* signal handler */
  	int32	sa_flags;		/* see signal options below */
-	int64	sa_mask;		/* signal mask to apply */
+	Sigset	sa_mask;		/* signal mask to apply */
  } Sigaction;
  
  void
@@ -141,7 +141,10 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
  	sa.sa_flags = SA_SIGINFO|SA_ONSTACK;\n \tif(restart)\n \t\tsa.sa_flags |= SA_RESTART;\n-\tsa.sa_mask = ~0ULL;\n+\tsa.sa_mask.__bits[0] = ~(uint32)0;\n+\tsa.sa_mask.__bits[1] = ~(uint32)0;\n+\tsa.sa_mask.__bits[2] = ~(uint32)0;\n+\tsa.sa_mask.__bits[3] = ~(uint32)0;\n  \tif (fn == runtime·sighandler)\n  	\tfn = (void*)runtime·sigtramp;\n  \tsa.__sigaction_u.__sa_sigaction = (void*)fn;

コアとなるコードの解説

上記の差分から、以下の2つの主要な変更点が見て取れます。

  1. Sigaction構造体のsa_maskメンバーの型変更:

    -	int64	sa_mask;		/* signal mask to apply */
    +	Sigset	sa_mask;		/* signal mask to apply */
    

    これは、Goランタイムが内部的に使用するSigaction構造体定義において、sa_maskフィールドの型をint64からSigsetに変更したものです。このSigset型は、FreeBSDのsigset_tの実際の構造(4つのunsigned intの配列)を正確に反映するように、defs_freebsd_GOARCHファイルで定義されています。これにより、Goランタイムがsigactionシステムコールに渡すデータ構造が、FreeBSDカーネルが期待する形式と一致するようになります。

  2. sa_maskの初期化方法の変更:

    -\tsa.sa_mask = ~0ULL;
    +\tsa.sa_mask.__bits[0] = ~(uint32)0;
    +\tsa.sa_mask.__bits[1] = ~(uint32)0;
    +\tsa.sa_mask.__bits[2] = ~(uint32)0;
    +\tsa.sa_mask.__bits[3] = ~(uint32)0;
    

    以前は、sa_maskint64型であったため、~0ULLという64ビットの全ビットを1にする値で初期化していました。しかし、sa_maskSigset型(内部的に__bits[4]というuint32の配列を持つ)に変更されたため、各uint32要素を個別に初期化する必要があります。 ~(uint32)0は、32ビットのunsigned intの全ビットを1にする値です。これを__bits配列の各要素に代入することで、合計128ビットのシグナルマスクがすべて1に設定されます。これは、シグナルハンドラが実行されている間、すべてのシグナルをブロックするという意図を正確にFreeBSDカーネルに伝えるための変更です。

これらの変更により、GoランタイムはFreeBSDのシグナル処理メカニズムと完全に互換性を持つようになり、FreeBSD上でのGoのビルドと実行に関する問題が解消されました。

関連リンク

参考にした情報源リンク