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

[インデックス 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など)を実現するために不可欠な要素です。

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版のSysProcAttrCloneflagsフィールドが追加されています。これにより、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)が使用されるようになりました。 この変更には以下の重要な意味があります。

  1. clone(2)の使用:

    • SYS_CLONEclone(2)システムコールに対応します。
    • RawSyscall6は、6つの引数を取るシステムコールを呼び出すためのGoの低レベルAPIです。clone(2)は多くの引数を取るため、RawSyscallではなくRawSyscall6が適切です。
  2. uintptr(SIGCHLD)|sys.Cloneflags:

    • これはclone(2)システムコールの第一引数であるflagsに渡される値です。
    • SIGCHLDは、子プロセスが終了した際に親プロセスにSIGCHLDシグナルを送信するようにclone(2)に指示するフラグです。これにより、Goのランタイムが子プロセスの終了を適切に処理できるようになります。
    • sys.Cloneflagsは、新しく追加されたSysProcAttr構造体のCloneflagsフィールドから取得される値です。これにより、Goプログラムのユーザーは、os/execパッケージを通じて外部プロセスを起動する際に、clone(2)に渡す追加のフラグを自由に指定できるようになります。例えば、特定のネームスペースを共有しないようにしたり、新しいPIDネームスペースを作成したりするなどの高度な制御が可能になります。
  3. SysProcAttr.Cloneflagsの追加:

    • SysProcAttr構造体にCloneflags uintptrフィールドが追加されました。uintptr型は、ポインタを保持できる符号なし整数型であり、システムコールのフラグのようなビットマスク値を表現するのに適しています。
    • このフィールドを通じて、Goのユーザーはos/exec.CmdSysProcAttrを設定することで、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

コアとなるコードの解説

  1. SysProcAttr構造体へのCloneflagsフィールドの追加:

    type SysProcAttr struct {
        // ... 既存のフィールド ...
        Cloneflags uintptr     // Flags for clone calls (Linux only)
    }
    

    この変更により、syscall.SysProcAttr構造体にCloneflagsという新しいフィールドが追加されました。このフィールドはuintptr型であり、clone(2)システムコールに渡すフラグのビットマスクを保持するために使用されます。Goのユーザーは、os/exec.CmdSysProcAttrを設定する際にこのフィールドに値を設定することで、clone(2)の挙動をカスタマイズできるようになります。

  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_FORKfork()システムコールに対応)を引数なしで呼び出していました。
    • 変更後は、SYS_CLONEclone(2)システムコールに対応)を呼び出すように変更されました。
    • RawSyscall6は、6つの引数を取るシステムコールを呼び出すためのGoの低レベル関数です。
    • uintptr(SIGCHLD)|sys.Cloneflagsclone(2)の第一引数(フラグ)として渡されます。
      • SIGCHLDは、子プロセスが終了した際に親プロセスにSIGCHLDシグナルを送信するように指定します。これは、Goのランタイムが子プロセスの終了を適切に監視し、リソースをクリーンアップするために重要です。
      • sys.Cloneflagsは、SysProcAttr構造体から取得されるユーザー指定のフラグです。これにより、Goプログラムのユーザーは、clone(2)の挙動を細かく制御できるようになります。例えば、CLONE_NEWPIDを指定して新しいPIDネームスペースを作成したり、CLONE_NEWNSを指定して新しいマウントネームスペースを作成したりすることが可能になります。

この変更により、Go言語はLinuxのプロセス生成において、より高度な制御と柔軟性を獲得し、コンテナ技術やサンドボックス環境との連携がより容易になりました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Linux man pages (clone(2), fork(2), signal(7))
  • Go言語のソースコード(src/pkg/syscall/exec_linux.go
  • GitHubのコミット履歴と関連するIssue (#6214)
  • 一般的なLinuxシステムプログラミングに関する知識