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

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

このコミットは、Go言語のランタイムにおけるNetBSDシステムコールsigaction構造体の定義に関する修正です。具体的には、sigaction構造体内の共用体メンバー名と、シグナルマスクsa_maskの型定義がNetBSDの実際の定義と一致するように変更されています。これにより、NetBSD上でのGoプログラムのシグナルハンドリングが正しく機能するようになります。

コミット

commit 38445ca08915c0fb755f9f15f99a3226ed7f45f4
Author: Joel Sing <jsing@google.com>
Date:   Fri May 11 03:48:16 2012 +1000

    runtime: fix netbsd sigaction struct
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/6198063

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

https://github.com/golang/go/commit/38445ca08915c0fb755f9f15f99a3226ed7f45f4

元コミット内容

runtime: fix netbsd sigaction struct

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6198063

変更の背景

Go言語のランタイムは、様々なオペレーティングシステム(OS)上で動作するように設計されています。OSごとにシステムコールやデータ構造の定義が異なるため、Goランタイムは各OSの特性に合わせて調整される必要があります。

このコミットの背景には、NetBSDにおけるsigactionシステムコールの構造体定義が、Goランタイム内で誤って実装されていたという問題があります。sigactionは、プロセスがシグナル(例えば、Ctrl+Cによる割り込みや、不正なメモリアクセスなど)を受け取った際の挙動を定義するために使用される重要なシステムコールです。Goランタイムは、ガーベージコレクションやスケジューリング、デバッグなどの内部処理のためにシグナルハンドリングを多用します。

NetBSDのsigaction構造体は、シグナルハンドラを指す共用体メンバーの名前(__sigaction_uではなく_sa_u)と、シグナルマスクsa_maskの型(単一のuint32ではなくuint32の配列)が、Goランタイム内の定義と異なっていました。この不一致が原因で、NetBSD上でGoプログラムがシグナルを正しく処理できず、予期せぬクラッシュや誤動作を引き起こす可能性がありました。このコミットは、このNetBSD固有の構造体の不一致を修正し、GoランタイムがNetBSD上で安定して動作するようにすることを目的としています。

前提知識の解説

シグナルとシグナルハンドリング

  • シグナル: オペレーティングシステムがプロセスに送信する非同期の通知です。プログラムの異常終了(例: セグメンテーション違反)、外部からの割り込み(例: Ctrl+C)、タイマーの満了など、様々なイベントをプロセスに伝えます。
  • シグナルハンドリング: プロセスがシグナルを受け取った際に、どのように応答するかを定義するメカニズムです。デフォルトの動作(プロセス終了など)を変更し、特定の関数(シグナルハンドラ)を実行させることができます。

sigactionシステムコール

sigactionはPOSIX標準で定義されているシステムコールで、特定のシグナルに対するアクション(挙動)を検査または変更するために使用されます。signalシステムコールよりも柔軟で、シグナルハンドラの登録、シグナルマスクの設定、シグナルハンドラ実行中の動作(例: シグナルハンドラ実行中にブロックするシグナル)などを細かく制御できます。

sigactionシステムコールは、以下のような構造体(struct sigaction)を引数として取ります。

struct sigaction {
    union {
        void (*sa_handler)(int);         /* シグナルハンドラ関数 */
        void (*sa_sigaction)(int, siginfo_t *, void *); /* 拡張シグナルハンドラ関数 */
    } sa_sigaction_u; // または他の名前
    sigset_t sa_mask;                   /* シグナルハンドラ実行中にブロックするシグナルマスク */
    int sa_flags;                        /* シグナルの動作を制御するフラグ */
};
  • sa_sigaction_u (または類似の共用体): シグナルハンドラ関数を指すポインタを格納します。sa_handlerはシンプルなハンドラ、sa_sigactionはより詳細な情報(siginfo_t)を受け取るハンドラです。
  • sa_mask: シグナルハンドラが実行されている間、追加でブロックされるシグナルのセットを指定します。これにより、シグナルハンドラが再入可能でない場合に、同じシグナルが再度発生して問題を引き起こすのを防ぎます。
  • sa_flags: シグナルの動作を制御する様々なフラグ(例: SA_SIGINFOsa_sigactionを使用、SA_RESTARTでシステムコールを再開など)を設定します。

sigset_tとシグナルマスク

sigset_tは、シグナルの集合を表すデータ型です。通常、ビットマスクとして実装され、各ビットが特定のシグナルに対応します。sa_maskはこのsigset_t型であり、シグナルハンドラが実行されている間にブロックされるシグナルを指定します。

GoランタイムとOS固有のコード

Go言語のランタイムは、OSのカーネルと直接やり取りする低レベルな部分を含んでいます。これには、メモリ管理、ゴルーチン(軽量スレッド)のスケジューリング、ネットワークI/O、そしてシグナルハンドリングなどが含まれます。これらの機能はOSのシステムコールに依存するため、Goのソースコードにはruntime/signal_netbsd_386.cruntime/signal_netbsd_amd64.cのように、特定のOSやアーキテクチャに特化したファイルが存在します。これらのファイルは、GoランタイムがそのOS上で正しく動作するために必要な、OS固有のシステムコールラッパーやデータ構造の定義を含んでいます。

技術的詳細

このコミットは、NetBSDのsigaction構造体のGoランタイム内でのC言語定義を、NetBSDカーネルの実際の定義と一致させることに焦点を当てています。

具体的には、以下の2つの主要な変更が行われています。

  1. 共用体メンバー名の変更:

    • 変更前: __sigaction_u
    • 変更後: _sa_u NetBSDのsigaction構造体では、シグナルハンドラを格納する共用体の名前が_sa_uとなっています。GoランタイムのCコードがこの名前と一致しない場合、コンパイルエラーや、より深刻な場合は実行時の未定義動作(メモリレイアウトの不一致による誤った値の読み書き)が発生する可能性があります。
  2. sa_maskの型変更:

    • 変更前: uint32 sa_mask; (またはuint64 for amd64)
    • 変更後: uint32 sa_mask[4]; NetBSDでは、sigset_t型が単一のuint32(またはuint64)ではなく、uint32の配列(通常は4要素)として定義されています。これは、より多くのシグナルをサポートするために、シグナルマスクが複数のワードにまたがることを意味します。Goランタイムがこれを単一のuint32として扱っていた場合、シグナルマスクのビットが正しく設定されず、特定のシグナルがブロックされない、あるいは意図しないシグナルがブロックされるといった問題が発生します。

これらの変更は、GoランタイムがNetBSDのシグナルハンドリングメカニズムと正しくインターフェースするためのものです。特に、sa_maskが配列になったことで、シグナルマスクを設定する際に各配列要素に~0U(全ビットを1にする)を代入する必要があります。これは、すべてのシグナルをブロックするという一般的な操作に対応します。

この修正により、GoプログラムはNetBSD上でシグナルを期待通りに処理できるようになり、より堅牢で安定した動作が保証されます。

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

このコミットでは、以下の2つのファイルが変更されています。

  • src/pkg/runtime/signal_netbsd_386.c (32ビットNetBSDアーキテクチャ用)
  • src/pkg/runtime/signal_netbsd_amd64.c (64ビットNetBSDアーキテクチャ用)

両ファイルで同様の変更が行われています。

src/pkg/runtime/signal_netbsd_386.c の変更点

--- a/src/pkg/runtime/signal_netbsd_386.c
+++ b/src/pkg/runtime/signal_netbsd_386.c
@@ -11,10 +11,10 @@ extern void runtime·sigtramp(void);\
 
 typedef struct sigaction {
 	union {
-		void    (*__sa_handler)(int32);\
-		void    (*__sa_sigaction)(int32, Siginfo*, void *);\
-	} __sigaction_u;\t\t/* signal handler */\
-	uint32	sa_mask;\t\t/* signal mask to apply */
+		void    (*_sa_handler)(int32);\
+		void    (*_sa_sigaction)(int32, Siginfo*, void *);\
+	} _sa_u;\t\t\t/* signal handler */
+	uint32	sa_mask[4];\t\t/* signal mask to apply */
 	int32	sa_flags;\t\t/* see signal options below */
 } Sigaction;\
 
@@ -124,9 +124,12 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)\
 	sa.sa_flags = SA_SIGINFO|SA_ONSTACK;\
 	if(restart)\
 		sa.sa_flags |= SA_RESTART;\
-	sa.sa_mask = ~0ULL;\
+	sa.sa_mask[0] = ~0U;\
+	sa.sa_mask[1] = ~0U;\
+	sa.sa_mask[2] = ~0U;\
+	sa.sa_mask[3] = ~0U;\
 	if (fn == runtime·sighandler)\
 		fn = (void*)runtime·sigtramp;\
-	sa.__sigaction_u.__sa_sigaction = (void*)fn;\
+	sa._sa_u._sa_sigaction = (void*)fn;\
 	runtime·sigaction(i, &sa, nil);\
 }

src/pkg/runtime/signal_netbsd_amd64.c の変更点

--- a/src/pkg/runtime/signal_netbsd_amd64.c
+++ b/src/pkg/runtime/signal_netbsd_amd64.c
@@ -11,10 +11,10 @@ extern void runtime·sigtramp(void);\
 
 typedef struct sigaction {\
 	union {\
-		void    (*__sa_handler)(int32);\
-		void    (*__sa_sigaction)(int32, Siginfo*, void *);\
-	} __sigaction_u;\t\t/* signal handler */\
-	uint32	sa_mask;\t\t/* signal mask to apply */
+		void    (*_sa_handler)(int32);\
+		void    (*_sa_sigaction)(int32, Siginfo*, void *);\
+	} _sa_u;\t\t\t/* signal handler */
+	uint32	sa_mask[4];\t\t/* signal mask to apply */
 	int32	sa_flags;\t\t/* see signal options below */
 } Sigaction;\
 
@@ -133,9 +133,12 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)\
 	sa.sa_flags = SA_SIGINFO|SA_ONSTACK;\
 	if(restart)\
 		sa.sa_flags |= SA_RESTART;\
-	sa.sa_mask = ~0ULL;\
+	sa.sa_mask[0] = ~0U;\
+	sa.sa_mask[1] = ~0U;\
+	sa.sa_mask[2] = ~0U;\
+	sa.sa_mask[3] = ~0U;\
 	if (fn == runtime·sighandler)\
 		fn = (void*)runtime·sigtramp;\
-	sa.__sigaction_u.__sa_sigaction = (void*)fn;\
+	sa._sa_u._sa_sigaction = (void*)fn;\
 	runtime·sigaction(i, &sa, nil);\
 }

コアとなるコードの解説

Sigaction 構造体の定義変更

両ファイルにおいて、Sigaction構造体の定義が変更されています。

  • 共用体メンバー名の変更:

    • __sigaction_u から _sa_u へ変更。
    • これにより、NetBSDのシステムヘッダファイルで定義されている実際のsigaction構造体の共用体メンバー名と一致させます。GoランタイムのCコードがOSのAPIと正しく連携するために不可欠な変更です。
  • sa_mask の型変更:

    • uint32 sa_mask; (32ビット版) または uint64 sa_mask; (64ビット版) から uint32 sa_mask[4]; へ変更。
    • NetBSDでは、シグナルマスクsigset_tuint32の配列として定義されているため、Goランタイムもそれに合わせて配列として扱う必要があります。これにより、より多くのシグナルを正確に表現できるようになります。

runtime·setsig 関数内の sa_mask 設定変更

runtime·setsig関数は、特定のシグナルに対するハンドラを設定するGoランタイム内部の関数です。この関数内で、Sigaction構造体のsa_maskフィールドを設定する部分が変更されています。

  • 変更前: sa.sa_mask = ~0ULL; (64ビット版では~0ULL、32ビット版では~0Uまたは類似)

    • これは、単一のuint32またはuint64変数に対して全ビットを1に設定し、すべてのシグナルをブロックしようとしていました。
  • 変更後:

    sa.sa_mask[0] = ~0U;
    sa.sa_mask[1] = ~0U;
    sa.sa_mask[2] = ~0U;
    sa.sa_mask[3] = ~0U;
    
    • sa_maskuint32の配列になったため、配列の各要素に対して~0U(符号なし32ビット整数の全ビットを1にする)を代入しています。これにより、シグナルマスクのすべてのビットがセットされ、シグナルハンドラが実行されている間、すべてのシグナルがブロックされるようになります。これは、シグナルハンドラの実行中に他のシグナルによる割り込みを防ぎ、ハンドラの処理を安全に行うための一般的なプラクティスです。

runtime·setsig 関数内のシグナルハンドラ設定変更

  • 変更前: sa.__sigaction_u.__sa_sigaction = (void*)fn;
  • 変更後: sa._sa_u._sa_sigaction = (void*)fn;
    • Sigaction構造体内の共用体メンバー名が__sigaction_uから_sa_uに変更されたことに伴い、シグナルハンドラ関数ポインタfnを代入する際のパスも変更されています。これにより、正しいメモリ位置にハンドラが設定されるようになります。

これらの変更は、NetBSDのシステムコールインターフェースとの互換性を確保し、GoランタイムがNetBSD上でシグナルを正しく処理できるようにするために不可欠です。

関連リンク

参考にした情報源リンク

  • Go CL 6198063: https://golang.org/cl/6198063 (コミットメッセージに記載されているGoのコードレビューシステムへのリンク)
  • NetBSDのsigaction構造体に関する情報(NetBSDのソースコードやドキュメントを参照)
    • 例: NetBSDのsys/signal.hヘッダファイル内のstruct sigaction定義
    • NetBSDのmanページ: man 2 sigaction (NetBSDシステムで実行)
  • 一般的なシグナルハンドリングに関する情報源(例: UNIXプログラミングに関する書籍やオンラインリソース)