[インデックス 10969] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにおいて、NetBSDオペレーティングシステムでのパイプ作成処理を改善するものです。具体的には、従来のpipe
システムコールから、より高機能で安全なpipe2
システムコールへの移行を行い、NetBSD環境でのパイプの動作を修正・最適化しています。
コミット
commit f1ebbf80bd8fcc6b38e958f170ee755cf28c27f7
Author: Joel Sing <jsing@google.com>
Date: Fri Dec 23 02:47:48 2011 +1100
syscall: make pipe work on netbsd
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5504070
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f1ebbf80bd8fcc6b38e958f170ee755cf28c27f7
元コミット内容
syscall: make pipe work on netbsd
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5504070
変更の背景
この変更の背景には、Unix系OSにおけるパイプ作成の進化と、Go言語のsyscall
パッケージが各OSのシステムコールを適切にラップする必要性があります。
従来のpipe()
システムコールは、パイプを作成する際にファイルディスクリプタを返しますが、そのファイルディスクリプタにO_CLOEXEC
(exec
時にクローズする)やO_NONBLOCK
(非ブロッキングI/O)といったフラグを設定するためには、パイプ作成後に別途fcntl()
システムコールを呼び出す必要がありました。この「パイプ作成」と「フラグ設定」の間に時間差が生じるため、マルチスレッド環境などでは競合状態(race condition)が発生する可能性がありました。例えば、パイプ作成後にfork()
とexec()
を呼び出すようなシナリオで、fcntl()
が実行される前にexec()
が呼び出されてしまうと、意図せずパイプのファイルディスクリプタが子プロセスに継承されてしまう、といった問題が起こり得ます。
pipe2()
システムコールは、このような競合状態を回避するために導入されました。pipe2()
は、パイプの作成と同時にフラグを設定できるため、アトミック(不可分)な操作としてパイプを初期化できます。これにより、セキュリティの向上(不要なファイルディスクリプタの継承防止)と、より堅牢なプロセス間通信の実現が可能になります。
NetBSDは、比較的新しいPOSIX標準に準拠するためにpipe2()
を導入しました。Go言語のsyscall
パッケージは、GoプログラムがOSの低レベル機能にアクセスするためのインターフェースを提供するため、各OSの最新かつ最適なシステムコールを利用するように更新される必要があります。このコミットは、NetBSD環境でpipe()
が抱えていた潜在的な問題を解決し、より現代的なpipe2()
を利用することで、Goプログラムの安定性と信頼性を向上させることを目的としています。
前提知識の解説
- パイプ (Pipe): Unix系OSにおけるプロセス間通信 (IPC: Interprocess Communication) の一種。単方向のデータストリームを提供する。通常、
pipe()
システムコールによって作成され、読み込み側と書き込み側の2つのファイルディスクリプタが生成される。 pipe()
システムコール: 従来のパイプ作成システムコール。2つのファイルディスクリプタ(読み込み用と書き込み用)を返す。pipe2()
システムコール:pipe()
の拡張版。パイプ作成時に追加のフラグ(例:O_CLOEXEC
,O_NONBLOCK
)をアトミックに設定できる。Linuxで最初に導入され、その後NetBSDなどの他のUnix系OSにも採用された。syscall
パッケージ (Go言語): Go言語の標準ライブラリの一部で、オペレーティングシステムの低レベルなシステムコールに直接アクセスするための機能を提供する。OS固有の定数や関数が含まれる。- NetBSD: オープンソースのUnix系オペレーティングシステムの一つ。移植性が高く、多くの異なるハードウェアアーキテクチャで動作する。
_C_int
(Go言語): Go言語のsyscall
パッケージで、C言語のint
型に対応する型。システムコール引数や戻り値の型として使用される。RawSyscall
(Go言語): Go言語のsyscall
パッケージで提供される関数の一つ。OSのシステムコールを直接呼び出すための低レベルなインターフェース。引数としてシステムコール番号と最大3つのuintptr
型の引数を取る。SYS_PIPE
/SYS_PIPE2
: それぞれpipe()
およびpipe2()
システムコールに対応するシステムコール番号。RawSyscall
関数で特定のシステムコールを呼び出す際に使用される。O_CLOEXEC
フラグ: ファイルディスクリプタに設定されるフラグの一つ。このフラグが設定されたファイルディスクリプタは、exec
系のシステムコール(新しいプログラムを実行する際に使われる)が呼び出されたときに自動的にクローズされる。これにより、子プロセスに不要なファイルディスクリプタが継承されるのを防ぎ、セキュリティとリソース管理を改善する。O_NONBLOCK
フラグ: ファイルディスクリプタに設定されるフラグの一つ。このフラグが設定されたファイルディスクリプタに対するI/O操作(読み込みや書き込み)は、データがすぐに利用可能でない場合や、すぐに書き込めない場合にブロックせず、エラーを返すようになる。
技術的詳細
このコミットは、Go言語のsyscall
パッケージがNetBSD上でパイプを作成する際の内部実装を変更しています。具体的には、Goのsyscall.Pipe
関数が、NetBSDのpipe()
システムコールではなく、pipe2()
システムコールを呼び出すように修正されています。
pipe2()
システムコールは、pipe()
と同様に2つのファイルディスクリプタ(読み込み側と書き込み側)を返しますが、追加のflags
引数を受け取ります。このflags
引数には、O_CLOEXEC
やO_NONBLOCK
などのビットマスクを指定できます。このコミットでは、pipe2(&pp, 0)
と呼び出されており、flags
引数に0
が渡されています。これは、現時点では特別なフラグを設定しないことを意味しますが、将来的に必要に応じてフラグを追加する柔軟性を提供します。
Goのsyscall
パッケージは、各OSのシステムコールをGoの関数としてラップしています。zsyscall_netbsd_386.go
とzsyscall_netbsd_amd64.go
のようなファイルは、Goのツールによって自動生成されるファイルであり、特定のアーキテクチャ(386やamd64)とOS(NetBSD)向けのシステムコールラッパーが含まれています。これらのファイルでは、RawSyscall
関数を使用して実際のOSシステムコールを呼び出しています。
この変更により、Goのsyscall.Pipe
関数がNetBSD上で呼び出された際に、内部的にSYS_PIPE
ではなくSYS_PIPE2
システムコールが使用されるようになります。これにより、NetBSDが提供するより現代的で堅牢なパイプ作成メカニズムが利用されることになります。
コアとなるコードの変更箇所
diff --git a/src/pkg/syscall/syscall_netbsd.go b/src/pkg/syscall/syscall_netbsd.go
index f122c62249..fcbf6157b8 100644
--- a/src/pkg/syscall/syscall_netbsd.go
+++ b/src/pkg/syscall/syscall_netbsd.go
@@ -62,13 +62,13 @@ func ParseDirent(buf []byte, max int, names []string) (consumed int, count int,
return origlen - len(buf), count, names
}
-//sysnb pipe(p *[2]_C_int) (err error)
+//sysnb pipe2(p *[2]_C_int, flags _C_int) (err error)
func Pipe(p []int) (err error) {
if len(p) != 2 {
return EINVAL
}
var pp [2]_C_int
- err = pipe(&pp)
+ err = pipe2(&pp, 0)
p[0] = int(pp[0])
p[1] = int(pp[1])
return
diff --git a/src/pkg/syscall/zsyscall_netbsd_386.go b/src/pkg/syscall/zsyscall_netbsd_386.go
index 61fb8b5b4f..6155a16878 100644
--- a/src/pkg/syscall/zsyscall_netbsd_386.go
+++ b/src/pkg/syscall/zsyscall_netbsd_386.go
@@ -253,8 +253,8 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func pipe(p *[2]_C_int) (err error) {
- _, _, e1 := RawSyscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
+func pipe2(p *[2]_C_int, flags _C_int) (err error) {
+ _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
diff --git a/src/pkg/syscall/zsyscall_netbsd_amd64.go b/src/pkg/syscall/zsyscall_netbsd_amd64.go
index e665aa0098..d68cd86d8e 100644
--- a/src/pkg/syscall/zsyscall_netbsd_amd64.go
+++ b/src/pkg/syscall/zsyscall_netbsd_amd64.go
@@ -253,8 +253,8 @@ func fcntl(fd int, cmd int, arg int) (val int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-func pipe(p *[2]_C_int) (err error) {
- _, _, e1 := RawSyscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
+func pipe2(p *[2]_C_int, flags _C_int) (err error) {
+ _, _, e1 := RawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
コアとなるコードの解説
このコミットは、Go言語のsyscall
パッケージにおけるNetBSD固有のパイプ作成ロジックを修正しています。
-
src/pkg/syscall/syscall_netbsd.go
の変更://sysnb pipe(p *[2]_C_int) (err error)
のコメントが//sysnb pipe2(p *[2]_C_int, flags _C_int) (err error)
に変更されています。これは、Goのsyscall
パッケージが内部的にpipe
という名前の関数をpipe2
という名前の関数にマッピングし、flags
引数を追加することを示唆しています。func Pipe(p []int)
関数内で、err = pipe(&pp)
の呼び出しがerr = pipe2(&pp, 0)
に変更されています。これは、GoのPipe
関数が、NetBSDのシステムコールを呼び出す際に、従来のpipe
システムコールではなく、pipe2
システムコールを呼び出すように変更されたことを意味します。0
というフラグは、現時点では特別なフラグを設定しないことを示しています。
-
src/pkg/syscall/zsyscall_netbsd_386.go
およびsrc/pkg/syscall/zsyscall_netbsd_amd64.go
の変更:- これらのファイルは、Goのツールによって自動生成されるシステムコールラッパーです。
func pipe(p *[2]_C_int) (err error)
の関数定義がfunc pipe2(p *[2]_C_int, flags _C_int) (err error)
に変更されています。これにより、pipe
という名前のGo関数がpipe2
という名前に変更され、flags
引数を受け取るようになりました。RawSyscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
の呼び出しがRawSyscall(SYS_PIPE2, uintptr(unsafe.Pointer(p)), uintptr(flags), 0)
に変更されています。これは、実際にOSのシステムコールを呼び出す際に、SYS_PIPE
(pipe
システムコールに対応する番号)ではなく、SYS_PIPE2
(pipe2
システムコールに対応する番号)を使用するように変更されたことを意味します。また、flags
引数がRawSyscall
に渡されるようになりました。
これらの変更により、Goのsyscall.Pipe
関数がNetBSD上で実行される際に、内部的にpipe2
システムコールが呼び出され、より現代的で安全なパイプ作成メカニズムが利用されるようになります。
関連リンク
- Go Code Review: https://golang.org/cl/5504070
参考にした情報源リンク
pipe()
andpipe2()
man pages (general Unix/Linux):- NetBSD
pipe2()
man page: - Go
os.Pipe()
documentation: - Stack Overflow discussions on
pipe()
vspipe2()
: - Go
syscall
package overview: - Go
golang.org/x/sys
repository (for context onsyscall
package evolution):