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

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

このコミットは、Go言語の標準ライブラリ net/http/cgi パッケージ内のテストコードが、Plan 9オペレーティングシステム上でコンパイルできるようにするための修正です。具体的には、POSIXシステムに特有の syscall.Signal(0) の参照を削除し、代わりにビルドタグ (+build) を使用して、プラットフォームごとに異なるプロセス実行チェックロジックを適用しています。これにより、テストコードのポータビリティが向上し、Plan 9環境でもテストが実行可能になります。

コミット

commit e3ed4cace07150cd766dd81d3dfbadffd2cde7b3
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Mon Feb 25 13:27:15 2013 -0800

    net/http/cgi: make tests compile on plan9
    
    Don't reference the non-portable syscall.Signal(0).
    
    Maybe they'll pass too. Untested. plan9 bit from
    Akshat Kumar.
    
    R=golang-dev, akumar
    CC=golang-dev
    https://golang.org/cl/7370049

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

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

元コミット内容

net/http/cgi: make tests compile on plan9

Don't reference the non-portable syscall.Signal(0).

Maybe they'll pass too. Untested. plan9 bit from Akshat Kumar.

変更の背景

この変更の背景には、Go言語のテストコードが特定のオペレーティングシステム(この場合はPlan 9)でコンパイルできないという問題がありました。具体的には、src/pkg/net/http/cgi/host_test.go 内でプロセスが実行中であるかを確認するために syscall.Signal(0) という呼び出しが使用されていました。

syscall.Signal(0) は、プロセスにシグナル0を送信することで、そのプロセスが存在し、シグナルを受信できるかどうかを確認するPOSIXシステム(Unix系OS)の一般的な手法です。しかし、Plan 9オペレーティングシステムでは、この syscall.Signal(0) の概念や実装が存在しないため、Plan 9上でGoのテストをビルドしようとするとコンパイルエラーが発生していました。

このコミットは、このポータビリティの問題を解決し、Goのテストスイートがより多くのプラットフォームで動作するようにするために行われました。Akshat Kumar氏からのPlan 9に関する情報提供がこの修正に貢献しています。

前提知識の解説

Plan 9 from Bell Labs

Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。Unixの後継として設計され、ネットワーク透過性、ファイルシステム中心の設計、UTF-8の採用など、多くの革新的な概念を導入しました。Plan 9は、Unixとは異なるシステムコールやプロセス管理のメカニズムを持っているため、Unix/POSIX環境で書かれたコードをそのまま実行することはできません。特に、プロセスが存在するかどうかを確認するメカニズムもUnixとは異なります。

syscall.Signal(0)

syscall.Signal(0) は、Go言語の syscall パッケージを通じて、指定されたプロセスID (PID) にシグナル0を送信する操作を指します。シグナル0は、実際には何もシグナルを送信せず、プロセスが存在し、シグナルを受信できる権限があるかどうかをチェックするために使用されます。

  • Unix/POSIXシステム: kill(pid, 0) システムコールに相当します。この呼び出しが成功すれば、そのPIDを持つプロセスが存在し、現在のユーザーがそのプロセスにシグナルを送信する権限を持っていることを意味します。エラーが発生した場合(例: ESRCH)、プロセスが存在しないことを示します。
  • 非POSIXシステム(例: Plan 9、Windows): これらのシステムでは、kill(pid, 0) に相当する直接的なAPIが存在しないか、異なるメカニズムでプロセスの存在を確認する必要があります。そのため、syscall.Signal(0) はポータブルではありません。

Go言語のビルドタグ (+build)

Go言語には、特定のファイルが特定の環境でのみコンパイルされるように制御するための「ビルドタグ」という機能があります。ソースファイルの先頭に // +build tagname の形式でコメントを記述することで、Goコンパイラはそのタグが有効な場合にのみファイルをコンパイルします。

  • // +build plan9: このタグが付いたファイルは、Plan 9環境でのみコンパイルされます。
  • // +build !plan9: このタグが付いたファイルは、Plan 9以外の環境(つまり、Plan 9タグが有効でない環境)でコンパイルされます。 この機能を利用することで、プラットフォーム固有のコードを分離し、ポータビリティを確保することができます。

技術的詳細

このコミットの技術的な核心は、syscall.Signal(0) の非ポータブル性を解決するために、Goのビルドタグとプラットフォーム固有のファイル分割を利用した点にあります。

  1. 共通インターフェースの導入: host_test.go 内で直接 syscall.Signal(0) を呼び出す代わりに、isProcessRunning(t, pid int) bool という新しい関数を導入しました。この関数は、プロセスID pid が現在実行中であるかどうかを判定し、その結果をブール値で返します。これにより、host_test.go は特定のOSのプロセスチェックメカニズムに依存しなくなりました。

  2. プラットフォーム固有の実装: isProcessRunning 関数の具体的な実装は、以下の2つの新しいファイルに分割されました。

    • src/pkg/net/http/cgi/plan9_test.go: このファイルには // +build plan9 というビルドタグが付与されています。Plan 9環境では、/proc/<pid> のようなパスが存在し、そのパスのファイル統計情報を取得できるかどうかでプロセスの存在を確認するのが一般的です。したがって、このファイル内の isProcessRunning 関数は os.Stat("/proc/" + strconv.Itoa(pid)) を使用してプロセスの存在をチェックします。os.Stat がエラーを返さなければ、プロセスは実行中と判断されます。
    • src/pkg/net/http/cgi/posix_test.go: このファイルには // +build !plan9 というビルドタグが付与されています。これは、Plan 9以外のすべての環境(主にPOSIXシステム)でコンパイルされることを意味します。このファイル内の isProcessRunning 関数は、従来の os.FindProcess(pid) でプロセスオブジェクトを取得し、その p.Signal(syscall.Signal(0)) == nil を呼び出すことでプロセスの実行状態を確認します。

このアプローチにより、Goコンパイラはビルド対象のOSに応じて適切な isProcessRunning の実装を選択し、コンパイルエラーを回避できるようになりました。

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

src/pkg/net/http/cgi/host_test.go

  • syscall パッケージのインポートが削除されました。
  • childRunning 無名関数内で、p.Signal(syscall.Signal(0)) == nil の呼び出しが isProcessRunning(t, pid) に置き換えられました。
--- a/src/pkg/net/http/cgi/host_test.go
+++ b/src/pkg/net/http/cgi/host_test.go
@@ -19,7 +19,6 @@ import (
 	"runtime"
 	"strconv"
 	"strings"
-	"syscall"
 	"testing"
 	"time"
 )
@@ -340,11 +339,7 @@ func TestCopyError(t *testing.T) {
 	}
 
 	childRunning := func() bool {
-		p, err := os.FindProcess(pid)
-		if err != nil {
-			return false
-		}
-		return p.Signal(syscall.Signal(0)) == nil
+		return isProcessRunning(t, pid)
 	}
 
 	if !childRunning() {

src/pkg/net/http/cgi/plan9_test.go (新規ファイル)

  • // +build plan9 ビルドタグが追加されました。
  • Plan 9環境向けの isProcessRunning 関数が定義されました。
--- /dev/null
+++ b/src/pkg/net/http/cgi/plan9_test.go
@@ -0,0 +1,18 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build plan9
+
+package cgi
+
+import (
+	"os"
+	"strconv"
+	"testing"
+)
+
+func isProcessRunning(t *testing.T, pid int) bool {
+	_, err := os.Stat("/proc/" + strconv.Itoa(pid))
+	return err == nil
+}

src/pkg/net/http/cgi/posix_test.go (新規ファイル)

  • // +build !plan9 ビルドタグが追加されました。
  • Plan 9以外の環境(POSIX)向けの isProcessRunning 関数が定義されました。
--- /dev/null
+++ b/src/pkg/net/http/cgi/posix_test.go
@@ -0,0 +1,21 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !plan9
+
+package cgi
+
+import (
+	"os"
+	"syscall"
+	"testing"
+)
+
+func isProcessRunning(t *testing.T, pid int) bool {
+	p, err := os.FindProcess(pid)
+	if err != nil {
+		return false
+	}
+	return p.Signal(syscall.Signal(0)) == nil
+}

コアとなるコードの解説

このコミットの核となるのは、isProcessRunning という関数の導入と、そのプラットフォームごとの実装の分離です。

isProcessRunning 関数の役割

isProcessRunning 関数は、与えられたプロセスID (PID) を持つプロセスが現在実行中であるかどうかを判定するための抽象化されたインターフェースを提供します。これにより、host_test.go のような共通のテストロジックは、基盤となるOSのプロセス管理の詳細を知る必要がなくなります。

plan9_test.go における実装

func isProcessRunning(t *testing.T, pid int) bool {
	_, err := os.Stat("/proc/" + strconv.Itoa(pid))
	return err == nil
}

Plan 9では、実行中のプロセスは /proc ファイルシステム内に対応するディレクトリ(例: /proc/12345)を持ちます。この実装では、os.Stat 関数を使用して、指定されたPIDに対応する /proc エントリが存在するかどうかを確認しています。os.Stat がエラーを返さなければ(つまり、ファイルが存在すれば)、プロセスは実行中であると判断されます。これはPlan 9における一般的なプロセスの存在確認方法です。

posix_test.go における実装

func isProcessRunning(t *testing.T, pid int) bool {
	p, err := os.FindProcess(pid)
	if err != nil {
		return false
	}
	return p.Signal(syscall.Signal(0)) == nil
}

Plan 9以外のシステム(主にLinuxやmacOSなどのPOSIX準拠システム)では、os.FindProcess(pid) を使用してプロセスオブジェクトを取得し、そのプロセスオブジェクトに対して Signal(syscall.Signal(0)) を呼び出すことでプロセスの存在を確認します。前述の通り、syscall.Signal(0) はシグナルを送信せずにプロセスの存在と権限をチェックするPOSIXの慣用的な方法です。この呼び出しがエラーなく成功すれば、プロセスは実行中であると判断されます。

このように、ビルドタグとプラットフォーム固有のファイルによって、同じ関数名 isProcessRunning を持ちながら、各OSの特性に合わせた最適なプロセスチェックロジックが適用されるようになっています。

関連リンク

参考にした情報源リンク