[インデックス 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のビルドタグとプラットフォーム固有のファイル分割を利用した点にあります。
-
共通インターフェースの導入:
host_test.go内で直接syscall.Signal(0)を呼び出す代わりに、isProcessRunning(t, pid int) boolという新しい関数を導入しました。この関数は、プロセスIDpidが現在実行中であるかどうかを判定し、その結果をブール値で返します。これにより、host_test.goは特定のOSのプロセスチェックメカニズムに依存しなくなりました。 -
プラットフォーム固有の実装:
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の特性に合わせた最適なプロセスチェックロジックが適用されるようになっています。
関連リンク
- Go Code Review: https://golang.org/cl/7370049
参考にした情報源リンク
- Go言語のビルド制約 (Build Constraints): https://pkg.go.dev/cmd/go#hdr-Build_constraints
- Plan 9 from Bell Labs: https://9p.io/plan9/
syscall.Signalのドキュメント (Go): https://pkg.go.dev/syscall#Signalkill(2)man page (Linux): https://man7.org/linux/man-pages/man2/kill.2.html (特にsigが0の場合の動作について)os.Statのドキュメント (Go): https://pkg.go.dev/os#Statos.FindProcessのドキュメント (Go): https://pkg.go.dev/os#FindProcess