[インデックス 17382] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにおけるLinux固有のプロセス生成メカニズムの変更に関するものです。具体的には、子プロセスの生成にfork()
システムコールではなくclone(2)
システムコールを使用するように変更し、SysProcAttr
構造体にCloneflags
フィールドを追加しています。これにより、より柔軟なプロセス生成オプションが提供されます。
コミット
commit 534c67abc425bc18ee95163c2266c1416b6f12c1
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date: Sat Aug 24 03:34:07 2013 +0200
syscall: add Cloneflags to Linux SysProcAttr.
Also use clone(2) syscall instead of fork().
Fixes #6214.
R=golang-dev, bradfitz, dave
CC=golang-dev
https://golang.org/cl/13159044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/534c67abc425bc18ee95163c2266c1416b6f12c1
元コミット内容
syscall: add Cloneflags to Linux SysProcAttr.
Also use clone(2) syscall instead of fork().
Fixes #6214.
R=golang-dev, bradfitz, dave
CC=golang-dev
https://golang.org/cl/13159044
変更の背景
この変更の背景には、GoプログラムがLinux上で外部プロセスを起動する際の柔軟性と制御の向上という目的があります。元の実装では、子プロセスの生成にfork()
システムコールが使用されていました。fork()
は親プロセスのメモリ空間やファイルディスクリプタなどをコピーして新しいプロセスを作成しますが、これは常に望ましい動作とは限りません。
特に、特定の共有リソース(例えば、メモリ空間、ファイルディスクリプタテーブル、シグナルハンドラ、マウントネームスペースなど)を親プロセスと子プロセス間で共有したい場合、fork()
では不十分です。clone(2)
システムコールは、これらの共有オプションを細かく制御できるため、より高度なプロセス生成シナリオに対応できます。
コミットメッセージにあるFixes #6214
は、この変更が特定のバグや機能不足を解決するために行われたことを示唆しています。#6214
は、おそらくclone(2)
のより詳細な制御が必要なケース、例えば特定のコンテナ技術やサンドボックス環境との連携、あるいは特定のシグナル処理の要件などに関連する問題であったと考えられます。
前提知識の解説
1. プロセス生成とシステムコール (fork()
vs clone(2)
)
-
fork()
システムコール:- Unix系OSにおける伝統的なプロセス生成メカニズムです。
- 呼び出し元のプロセス(親プロセス)のほぼ完全なコピーを作成し、新しいプロセス(子プロセス)を生成します。
- 子プロセスは親プロセスのメモリ空間、ファイルディスクリプタ、シグナルハンドラなどの多くの属性を継承しますが、これらは通常、コピーオンライト(Copy-on-Write, CoW)メカニズムによって効率的に管理されます。
fork()
の主な目的は、新しいプロセスが親プロセスとは独立して実行されるようにすることです。
-
clone(2)
システムコール:- Linux固有のシステムコールで、
fork()
よりもはるかに柔軟なプロセス(またはスレッド)生成メカニズムを提供します。 clone(2)
は、新しいプロセスが親プロセスと共有するリソースを細かく制御するための様々なフラグ(CLONE_VM
,CLONE_FS
,CLONE_FILES
,CLONE_SIGHAND
,CLONE_PARENT
,CLONE_NEWPID
など)を受け取ります。- これらのフラグを使用することで、新しいプロセスを完全に独立したプロセスとして作成することも、親プロセスと多くのリソースを共有する「スレッド」のようなエンティティとして作成することも可能です。
- 例えば、
CLONE_VM
フラグを指定すると、親と子が同じ仮想メモリ空間を共有します。これは、ユーザーレベルスレッドの実装などで利用されます。 clone(2)
は、コンテナ技術(Dockerなど)や仮想化技術において、ネームスペースの分離(CLONE_NEWPID
,CLONE_NEWNS
など)を実現するために不可欠な要素です。
- Linux固有のシステムコールで、
2. SIGCHLD
シグナル
SIGCHLD
(Signal Child)は、子プロセスの状態が変化したときに親プロセスに送信されるシグナルです。- 子プロセスが終了した、停止した、または再開した際に発生します。
- 親プロセスは
SIGCHLD
を捕捉することで、子プロセスの終了を検知し、wait()
やwaitpid()
などのシステムコールを呼び出して子プロセスの終了ステータスを回収し、ゾンビプロセス化を防ぐことができます。 clone(2)
システムコールでは、SIGCHLD
フラグを渡すことで、子プロセスが終了した際に親プロセスにSIGCHLD
シグナルを送信するように指定できます。これは、子プロセスのライフサイクル管理において重要です。
3. Go言語のsyscall
パッケージとSysProcAttr
-
syscall
パッケージ:- Go言語の標準ライブラリの一部で、オペレーティングシステムが提供する低レベルのシステムコールへのアクセスを提供します。
- これにより、GoプログラムはOSのカーネル機能と直接対話できます。
- OSに依存する機能(ファイル操作、プロセス管理、ネットワーク通信など)を抽象化せずに、OS固有のAPIを直接呼び出す必要がある場合に使用されます。
-
SysProcAttr
構造体:os/exec
パッケージを通じて外部コマンドを実行する際に、プロセス生成に関するOS固有の属性を設定するために使用される構造体です。- この構造体は、OSごとに異なるフィールドを持ち、例えばLinuxでは
Chroot
,Credential
,Pdeathsig
などのフィールドが含まれます。 - このコミットでは、Linux版の
SysProcAttr
にCloneflags
フィールドが追加されています。これにより、Goプログラムからclone(2)
システムコールに渡すフラグを直接指定できるようになります。
技術的詳細
このコミットの技術的な核心は、Go言語がLinux上で外部プロセスを起動する際の基盤となるシステムコールをfork()
からclone(2)
へ変更した点にあります。
変更前は、RawSyscall(SYS_FORK, 0, 0, 0)
が使用されていました。これは、fork()
システムコールを直接呼び出し、親プロセスのコピーを作成していました。
変更後、RawSyscall6(SYS_CLONE, uintptr(SIGCHLD)|sys.Cloneflags, 0, 0, 0, 0, 0)
が使用されるようになりました。
この変更には以下の重要な意味があります。
-
clone(2)
の使用:SYS_CLONE
はclone(2)
システムコールに対応します。RawSyscall6
は、6つの引数を取るシステムコールを呼び出すためのGoの低レベルAPIです。clone(2)
は多くの引数を取るため、RawSyscall
ではなくRawSyscall6
が適切です。
-
uintptr(SIGCHLD)|sys.Cloneflags
:- これは
clone(2)
システムコールの第一引数であるflags
に渡される値です。 SIGCHLD
は、子プロセスが終了した際に親プロセスにSIGCHLD
シグナルを送信するようにclone(2)
に指示するフラグです。これにより、Goのランタイムが子プロセスの終了を適切に処理できるようになります。sys.Cloneflags
は、新しく追加されたSysProcAttr
構造体のCloneflags
フィールドから取得される値です。これにより、Goプログラムのユーザーは、os/exec
パッケージを通じて外部プロセスを起動する際に、clone(2)
に渡す追加のフラグを自由に指定できるようになります。例えば、特定のネームスペースを共有しないようにしたり、新しいPIDネームスペースを作成したりするなどの高度な制御が可能になります。
- これは
-
SysProcAttr.Cloneflags
の追加:SysProcAttr
構造体にCloneflags uintptr
フィールドが追加されました。uintptr
型は、ポインタを保持できる符号なし整数型であり、システムコールのフラグのようなビットマスク値を表現するのに適しています。- このフィールドを通じて、Goのユーザーは
os/exec.Cmd
のSysProcAttr
を設定することで、clone(2)
の挙動をカスタマイズできます。
この変更により、GoプログラムはLinuxのclone(2)
システムコールの持つ柔軟性を最大限に活用できるようになり、コンテナ化された環境やサンドボックス化された環境でのプロセス管理、あるいは特定の共有リソースを持つプロセスの生成といった、より複雑なシナリオに対応する能力が向上しました。
コアとなるコードの変更箇所
変更はsrc/pkg/syscall/exec_linux.go
ファイルに集中しています。
--- a/src/pkg/syscall/exec_linux.go
+++ b/src/pkg/syscall/exec_linux.go
@@ -20,6 +20,7 @@ type SysProcAttr struct {
Noctty bool // Detach fd 0 from controlling terminal
Ctty int // Controlling TTY fd (Linux only)
Pdeathsig Signal // Signal that the process will get when its parent dies (Linux only)
+ Cloneflags uintptr // Flags for clone calls (Linux only)
}
// Implemented in runtime package.
@@ -61,7 +62,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
// About to call fork.
// No more allocation or calls of non-assembly functions.
runtime_BeforeFork()
- r1, _, err1 = RawSyscall(SYS_FORK, 0, 0, 0)
+ r1, _, err1 = RawSyscall6(SYS_CLONE, uintptr(SIGCHLD)|sys.Cloneflags, 0, 0, 0, 0, 0)
if err1 != 0 {
runtime_AfterFork()
return 0, err1
コアとなるコードの解説
-
SysProcAttr
構造体へのCloneflags
フィールドの追加:type SysProcAttr struct { // ... 既存のフィールド ... Cloneflags uintptr // Flags for clone calls (Linux only) }
この変更により、
syscall.SysProcAttr
構造体にCloneflags
という新しいフィールドが追加されました。このフィールドはuintptr
型であり、clone(2)
システムコールに渡すフラグのビットマスクを保持するために使用されます。Goのユーザーは、os/exec.Cmd
のSysProcAttr
を設定する際にこのフィールドに値を設定することで、clone(2)
の挙動をカスタマイズできるようになります。 -
forkAndExecInChild
関数内のシステムコール呼び出しの変更:// 変更前 // r1, _, err1 = RawSyscall(SYS_FORK, 0, 0, 0) // 変更後 r1, _, err1 = RawSyscall6(SYS_CLONE, uintptr(SIGCHLD)|sys.Cloneflags, 0, 0, 0, 0, 0)
forkAndExecInChild
関数は、Goのランタイムが新しいプロセスを生成する際に内部的に使用する関数です。- 変更前は、
SYS_FORK
(fork()
システムコールに対応)を引数なしで呼び出していました。 - 変更後は、
SYS_CLONE
(clone(2)
システムコールに対応)を呼び出すように変更されました。 RawSyscall6
は、6つの引数を取るシステムコールを呼び出すためのGoの低レベル関数です。uintptr(SIGCHLD)|sys.Cloneflags
がclone(2)
の第一引数(フラグ)として渡されます。SIGCHLD
は、子プロセスが終了した際に親プロセスにSIGCHLD
シグナルを送信するように指定します。これは、Goのランタイムが子プロセスの終了を適切に監視し、リソースをクリーンアップするために重要です。sys.Cloneflags
は、SysProcAttr
構造体から取得されるユーザー指定のフラグです。これにより、Goプログラムのユーザーは、clone(2)
の挙動を細かく制御できるようになります。例えば、CLONE_NEWPID
を指定して新しいPIDネームスペースを作成したり、CLONE_NEWNS
を指定して新しいマウントネームスペースを作成したりすることが可能になります。
- 変更前は、
この変更により、Go言語はLinuxのプロセス生成において、より高度な制御と柔軟性を獲得し、コンテナ技術やサンドボックス環境との連携がより容易になりました。
関連リンク
clone(2)
man page: https://man7.org/linux/man-pages/man2/clone.2.htmlfork(2)
man page: https://man7.org/linux/man-pages/man2/fork.2.htmlSIGCHLD
man page: https://man7.org/linux/man-pages/man7/signal.7.html- Go
os/exec
package documentation: https://pkg.go.dev/os/exec - Go
syscall
package documentation: https://pkg.go.dev/syscall
参考にした情報源リンク
- Go言語の公式ドキュメント
- Linux man pages (
clone(2)
,fork(2)
,signal(7)
) - Go言語のソースコード(
src/pkg/syscall/exec_linux.go
) - GitHubのコミット履歴と関連するIssue (
#6214
) - 一般的なLinuxシステムプログラミングに関する知識