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

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

このコミットは、GoランタイムにおけるOpenBSDのビルド問題を修正するものです。具体的には、runtime·unblocksignals 関数内の runtime·sigprocmask の呼び出しにおいて、sigset_none 引数の渡し方を修正しています。

コミット

commit eb7ed0d6264b76a59e564803aed16fc4ccacd4d9
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Thu Dec 19 21:12:18 2013 -0500

    runtime: fix build for OpenBSD
    
    R=golang-dev
    CC=golang-dev
    https://golang.org/cl/38030045

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

https://github.com/golang/go/commit/eb7ed0d6264b76a59e564803aed16fc4ccacd4d9

元コミット内容

このコミットの目的は、GoランタイムのOpenBSDビルドにおける問題を修正することです。コミットメッセージは簡潔に「runtime: fix build for OpenBSD」と述べており、具体的な修正内容についてはコードの変更差分を見る必要があります。

変更の背景

Goランタイムは、様々なオペレーティングシステム(OS)上で動作するように設計されています。各OSには独自のシステムコールやAPIが存在するため、GoランタイムはOS固有のコード(OSレイヤー)を持つ必要があります。OpenBSDもその一つであり、src/pkg/runtime/os_openbsd.c のようなファイルでOS固有の実装が提供されています。

このコミットが行われた2013年12月時点では、GoランタイムのOpenBSD向けビルドプロセスにおいて、シグナルマスクの設定に関する問題が発生していたと考えられます。具体的には、runtime·sigprocmask 関数への引数の渡し方がOpenBSDの期待する形式と異なっていたため、コンパイルエラーや不正な動作を引き起こしていた可能性があります。

シグナルマスクは、プロセスが受信するシグナルを制御するための重要なメカニズムです。Goランタイムのような低レベルのシステムプログラミングでは、シグナルハンドリングは非常に重要であり、OSの仕様に厳密に従う必要があります。この問題は、OpenBSD環境でのGoプログラムの安定性や実行可能性に直接影響を与えるため、修正が必要でした。

前提知識の解説

シグナル (Signals)

Unix系OSにおけるシグナルは、プロセスに対して非同期的にイベントを通知するソフトウェア割り込みの一種です。例えば、Ctrl+Cを押すと SIGINT シグナルがプロセスに送られ、プロセスを終了させることができます。シグナルには様々な種類があり、それぞれ異なるイベント(例: SIGSEGV はセグメンテーション違反、SIGTERM は終了要求)に対応しています。

シグナルマスク (Signal Mask)

各プロセスは「シグナルマスク」と呼ばれるビットマスクを持っています。これは、現在ブロックされている(つまり、受信してもすぐに処理されない)シグナルのセットを定義します。シグナルがブロックされている間にそのシグナルが配送されると、そのシグナルは保留状態となり、シグナルマスクからブロックが解除されたときに処理されます。

sigprocmask システムコール

sigprocmask は、プロセスのシグナルマスクを検査または変更するためのPOSIX標準のシステムコールです。この関数は通常、以下の3つの引数を取ります。

  1. how: シグナルマスクをどのように変更するかを指定します。
    • SIG_BLOCK: 現在のシグナルマスクに set で指定されたシグナルを追加します。
    • SIG_UNBLOCK: 現在のシグナルマスクから set で指定されたシグナルを削除します。
    • SIG_SETMASK: 現在のシグナルマスクを set で指定されたシグナルセットに置き換えます。
  2. set: 変更するシグナルのセットを含む sigset_t 型のポインタです。
  3. oldset: 変更前のシグナルマスクを保存するための sigset_t 型のポインタです。通常、不要な場合は NULL を渡します。

sigset_t

sigset_t は、シグナルのセットを表すために使用されるデータ型です。これは通常、ビットマスクとして実装されており、各ビットが特定のシグナルに対応します。sigset_none は、どのシグナルも含まない空のシグナルセットを表すグローバル変数または定数です。

Goランタイムにおけるシグナルハンドリング

Goランタイムは、ガベージコレクション、スケジューリング、デッドロック検出など、多くの内部処理でシグナルを利用します。そのため、OSレベルでのシグナルハンドリングを正確に行うことは、Goプログラムの安定性とパフォーマンスにとって不可欠です。runtime·unblocksignals のような関数は、Goランタイムが特定の操作を行う際に、一時的にシグナルをブロックしたり、ブロックを解除したりするために使用されます。

技術的詳細

このコミットの技術的な核心は、sigprocmask システムコールへの引数の渡し方に関するものです。

元のコードでは、runtime·sigprocmask(SIG_SETMASK, &sigset_none); となっていました。ここで &sigset_nonesigset_none のアドレス(ポインタ)を渡しています。

しかし、修正後のコードでは、runtime·sigprocmask(SIG_SETMASK, sigset_none); となっています。これは sigset_none を直接渡しています。

この変更は、runtime·sigprocmask のGoランタイム内部での定義、またはOpenBSDの特定のバージョンにおける sigprocmask のラッパー関数の期待する引数の型に起因すると考えられます。

一般的に、POSIXの sigprocmaskconst sigset_t *set を2番目の引数として期待します。つまり、sigset_t 型のポインタを渡すのが標準です。しかし、GoランタイムはC言語で書かれた部分(os_openbsd.c など)とGo言語で書かれた部分が混在しており、GoのFFI (Foreign Function Interface) や特定のOSのシステムコールラッパーの実装によっては、ポインタのポインタ(**sigset_t)ではなく、値渡し(sigset_t)を期待する場合があります。

このケースでは、OpenBSDのGoランタイムビルドにおいて、runtime·sigprocmask の内部実装が sigset_none の「値」を直接受け取るように定義されていたか、あるいはOpenBSDの特定のヘッダーファイルやライブラリが sigprocmask のラッパー関数を定義しており、それが sigset_t の値を期待していた可能性が高いです。

sigset_none は通常、グローバルな sigset_t 型の変数であり、その内容は変更されない(空のシグナルセット)ため、ポインタで渡すか値で渡すかは、コンパイラやリンカ、あるいはGoランタイムの内部的な規約に依存します。この修正は、OpenBSD環境でのコンパイルエラーを解消するための、型の一致に関する修正であると推測されます。

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

--- a/src/pkg/runtime/os_openbsd.c
+++ b/src/pkg/runtime/os_openbsd.c
@@ -304,5 +304,5 @@ runtime·signalstack(byte *p, int32 n)
 void
 runtime·unblocksignals(void)
 {
-	runtime·sigprocmask(SIG_SETMASK, &sigset_none);
+	runtime·sigprocmask(SIG_SETMASK, sigset_none);
 }

コアとなるコードの解説

変更は src/pkg/runtime/os_openbsd.c ファイルの runtime·unblocksignals 関数内で行われています。

変更前:

void
runtime·unblocksignals(void)
{
	runtime·sigprocmask(SIG_SETMASK, &sigset_none);
}

runtime·unblocksignals 関数は、すべてのシグナルのブロックを解除するために呼び出されます。これは、Goランタイムがシグナルを処理できる状態に戻すために行われます。 変更前は、runtime·sigprocmask 関数に SIG_SETMASK (シグナルマスクを置き換えるモード) と、sigset_none のアドレス (&sigset_none) が渡されていました。これは、sigset_none が指す空のシグナルセットで現在のシグナルマスクを置き換えることを意図しています。

変更後:

void
runtime·unblocksignals(void)
{
	runtime·sigprocmask(SIG_SETMASK, sigset_none);
}

変更後では、runtime·sigprocmask 関数に sigset_none が直接渡されています。これは、sigset_none の「値」が引数として渡されることを意味します。

この修正は、OpenBSD環境におけるGoランタイムのビルドシステムまたはコンパイラが、runtime·sigprocmask の2番目の引数として sigset_t 型のポインタではなく、sigset_t 型の値を期待していたためと考えられます。C言語では、配列や構造体を関数に渡す際に、ポインタで渡すか値で渡すかで挙動が異なります。この場合、sigset_none がポインタとしてではなく、値として渡されることで、OpenBSDのビルド要件を満たし、コンパイルエラーを解消したと推測されます。

関連リンク

参考にした情報源リンク