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

[インデックス 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_CLOEXECexec時にクローズする)や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_CLOEXECO_NONBLOCKなどのビットマスクを指定できます。このコミットでは、pipe2(&pp, 0)と呼び出されており、flags引数に0が渡されています。これは、現時点では特別なフラグを設定しないことを意味しますが、将来的に必要に応じてフラグを追加する柔軟性を提供します。

Goのsyscallパッケージは、各OSのシステムコールをGoの関数としてラップしています。zsyscall_netbsd_386.gozsyscall_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固有のパイプ作成ロジックを修正しています。

  1. 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というフラグは、現時点では特別なフラグを設定しないことを示しています。
  2. 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_PIPEpipeシステムコールに対応する番号)ではなく、SYS_PIPE2pipe2システムコールに対応する番号)を使用するように変更されたことを意味します。また、flags引数がRawSyscallに渡されるようになりました。

これらの変更により、Goのsyscall.Pipe関数がNetBSD上で実行される際に、内部的にpipe2システムコールが呼び出され、より現代的で安全なパイプ作成メカニズムが利用されるようになります。

関連リンク

参考にした情報源リンク