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

[インデックス 11052] ファイルの概要

このコミットは、Go言語のsyscallパッケージにおいて、Linux固有の「親プロセス死亡シグナル (parent death signal)」のサポートを追加するものです。これにより、子プロセスが親プロセスの終了時に特定のシグナルを受け取るように設定できるようになり、プロセスのライフサイクル管理がより堅牢になります。

コミット

  • コミットハッシュ: 2cb6fcf63f9f7f87ffa78b86a23bf33fc19e76ad
  • 作者: Albert Strasheim fullung@gmail.com
  • コミット日時: 2012年1月9日 月曜日 21:37:46 +0900

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/2cb6fcf63f9f7f87ffa78b86a23bf33fc19e76ad

元コミット内容

syscall: Linux-only support for parent death signal

As discussed in this thread:

https://groups.google.com/group/golang-dev/browse_thread/thread/5b76b7700265a787

I've tried to come up with a solution that is minimally invasive for the platforms that don't support "parent death signal", without splitting up exec_unix.go.

See also: http://www.win.tue.nl/~aeb/linux/lk/lk-5.html#ss5.8

R=rsc, dave, borman, iant, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/5487061

変更の背景

この変更の背景には、Go言語で起動された子プロセスが、その親プロセスが予期せず終了した場合に「孤児」となり、システムリソースを消費し続ける可能性があるという問題がありました。特に、親プロセスがクラッシュしたり、適切に子プロセスを終了させずに終了したりする場合に、ゾンビプロセスやデッドロックの原因となることが考えられます。

コミットメッセージで言及されているGoogle Groupsのスレッド(https://groups.google.com/group/golang-dev/browse_thread/thread/5b76b7700265a787)では、この問題と、Linuxカーネルが提供するPR_SET_PDEATHSIGという機能を使ってこれを解決する可能性について議論されています。PR_SET_PDEATHSIGは、親プロセスが終了した際に、子プロセスに特定のシグナル(例えばSIGTERMSIGKILL)を送信するように設定できる機能です。これにより、親プロセスが終了した際に子プロセスも自動的に終了するように促すことができ、リソースリークを防ぎ、システムの健全性を保つことができます。

このコミットの目的は、exec_unix.goを分割することなく、親プロセス死亡シグナルをサポートしないプラットフォームへの影響を最小限に抑えつつ、Linux環境でのこの重要な機能を提供することでした。

前提知識の解説

1. プロセスと親子関係

Unix系OSでは、プロセスは階層構造を持っています。新しいプロセスは既存のプロセス(親プロセス)によって作成され、作成されたプロセスは子プロセスとなります。子プロセスは親プロセスから多くの属性(環境変数、開いているファイルディスクリプタなど)を継承します。

2. fork()exec()システムコール

  • fork(): 既存のプロセス(親プロセス)のほぼ完全なコピーである新しいプロセス(子プロセス)を作成するシステムコールです。fork()が成功すると、親プロセスと子プロセスの両方で実行が継続されますが、戻り値が異なります(親プロセスには子プロセスのPID、子プロセスには0が返されます)。
  • exec()ファミリー: execファミリーのシステムコール(例: execve, execl, execvpなど)は、現在のプロセスイメージを新しいプログラムで置き換えます。これにより、新しいプログラムが現在のプロセスのコンテキスト(PID、開いているファイルディスクリプタなど)で実行されます。通常、新しいプログラムを起動する際には、まずfork()で子プロセスを作成し、その子プロセス内でexec()を呼び出して新しいプログラムを実行します。

3. シグナル

シグナルは、Unix系OSにおけるプロセス間通信の一種で、特定のイベントが発生したことをプロセスに通知するソフトウェア割り込みのようなものです。例えば、SIGTERMはプロセスに終了を要求するシグナル、SIGKILLはプロセスを強制終了するシグナルです。

4. prctl()システムコールとPR_SET_PDEATHSIG

prctl()(process control)は、Linux固有のシステムコールで、呼び出し元プロセスの動作や属性を制御するために使用されます。様々な操作が可能ですが、このコミットで重要なのはPR_SET_PDEATHSIGオプションです。

  • PR_SET_PDEATHSIG: このオプションは、現在のプロセス(子プロセス)の親プロセスが終了したときに、指定されたシグナルを子プロセスに送信するように設定します。これにより、親プロセスが予期せず終了した場合でも、子プロセスが孤児として残り続けることを防ぎ、自動的に終了させることができます。

5. ファイルディスクリプタ (File Descriptor, FD)

ファイルディスクリプタは、Unix系OSにおいてファイルやI/Oリソース(ソケット、パイプなど)を識別するために使用される整数値です。プロセスがfork()されると、子プロセスは親プロセスの開いているファイルディスクリプタを継承します。

6. CLOEXEC (Close-on-exec) フラグ

ファイルディスクリプタにはCLOEXECというフラグを設定できます。このフラグが設定されているファイルディスクリプタは、exec()システムコールが成功した際に自動的に閉じられます。これは、子プロセスが不要なファイルディスクリプタを継承してしまわないようにするために重要です。

技術的詳細

このコミットの主要な技術的詳細は、Linuxカーネルが提供するPR_SET_PDEATHSIG機能のGo言語syscallパッケージへの統合です。

Go言語のos/execパッケージは、内部的にsyscallパッケージを利用して新しいプロセスを起動します。プロセス起動の核心は、forkexecの組み合わせです。このコミット以前は、exec_unix.goというファイルがUnix系OS全般のforkAndExecInChild関数を実装していました。しかし、PR_SET_PDEATHSIGはLinux固有の機能であるため、既存のexec_unix.goを直接変更すると、他のUnix系OS(Darwin, FreeBSD, NetBSD, OpenBSDなど)に不要なコードが混入してしまいます。

この問題を解決するために、コミットでは以下の戦略が取られました。

  1. プラットフォーム固有のファイル分割:

    • 既存のsrc/pkg/syscall/exec_unix.goから、forkAndExecInChild関数の実装を削除しました。
    • 新しくsrc/pkg/syscall/exec_bsd.gosrc/pkg/syscall/exec_linux.goというファイルが作成されました。
    • exec_bsd.goは、Darwin, FreeBSD, NetBSD, OpenBSDなどのBSD系のOS向けに、従来のexec_unix.goにあったforkAndExecInChildの汎用的な実装を含みます。このファイルはビルドタグ// +build darwin freebsd netbsd openbsdによって、これらのOSでのみコンパイルされるように制御されます。
    • exec_linux.goは、Linux向けにforkAndExecInChildのLinux固有の実装を含みます。このファイルはビルドタグ// +build linuxによって、Linuxでのみコンパイルされるように制御されます。
  2. SysProcAttr構造体の拡張:

    • src/pkg/syscall/exec_linux.goに定義されるSysProcAttr構造体(プロセス起動時のシステム固有の属性を保持する)に、Pdeathsig intという新しいフィールドが追加されました。このフィールドは、親プロセスが終了した際に子プロセスに送信されるシグナル番号を指定します。exec_bsd.goにはこのフィールドは追加されず、Linux固有の機能であることを明確にしています。
  3. PR_SET_PDEATHSIGの呼び出し:

    • exec_linux.go内のforkAndExecInChild関数内で、sys.Pdeathsigが0でない場合にprctl(PR_SET_PDEATHSIG, sys.Pdeathsig)システムコールが呼び出されるようになりました。これにより、子プロセスは親プロセスの終了時に指定されたシグナルを受け取るように設定されます。
    • さらに、PR_SET_PDEATHSIGを設定した直後に、親プロセスが既に死亡している可能性を考慮してGETPPIDで親のPIDを取得し、それが1(initプロセス)であれば、自身にPdeathsigで指定されたシグナルを送信するロジックが追加されています。これは、親プロセスがPR_SET_PDEATHSIGを設定する前に終了してしまった場合のコーナーケースに対応するためです。
  4. ビルドスクリプトとMakefileの更新:

    • 各OS向けのビルドスクリプト(例: src/buildscript_linux_386.shなど)が更新され、exec_unix.goの代わりに、それぞれのOSに対応するexec_bsd.goまたはexec_linux.goがコンパイル対象に含まれるように変更されました。
    • src/pkg/syscall/Makefileも更新され、GOFILES_darwin, GOFILES_freebsd, GOFILES_linux, GOFILES_netbsd, GOFILES_openbsdの各変数に、それぞれのプラットフォームでコンパイルされるべきファイルが適切にリストされるようになりました。特に、Linux関連の定義にはexec_linux.goが、BSD関連の定義にはexec_bsd.goが追加されています。
  5. エラー定数の追加:

    • src/pkg/syscall/mkerrors.shが更新され、prctlシステムコールに関連する定数(PR_SET_PDEATHSIGなど)がGoのsyscallパッケージで利用できるように、zerrors_linux_*.goファイルに自動生成されるようになりました。これにより、Goコードからこれらの定数を安全に参照できるようになります。

この変更により、GoアプリケーションはLinux上で子プロセスを起動する際に、親プロセスの死亡を検知し、適切に子プロセスを終了させるメカニズムを組み込むことができるようになりました。これは、長期実行されるサーバーアプリケーションや、複数のプロセスが連携して動作するシステムにおいて、リソース管理と安定性を向上させる上で非常に重要です。

コアとなるコードの変更箇所

このコミットにおける主要なコード変更は以下のファイルに集中しています。

  • src/pkg/syscall/exec_unix.go:
    • forkAndExecInChild関数の実装が削除されました。このファイルは、プラットフォーム固有の実装に置き換えられるための汎用的な定義のみを残す形になりました。
  • src/pkg/syscall/exec_bsd.go (新規作成):
    • BSD系OS(Darwin, FreeBSD, NetBSD, OpenBSD)向けのforkAndExecInChild関数の実装が追加されました。これは、従来のexec_unix.goにあった汎用的なロジックを継承しています。
    • SysProcAttr構造体が定義されていますが、Pdeathsigフィールドは含まれません。
  • src/pkg/syscall/exec_linux.go (新規作成):
    • Linux向けのforkAndExecInChild関数の実装が追加されました。この実装には、PR_SET_PDEATHSIGシステムコールを呼び出すロジックが含まれています。
    • SysProcAttr構造体にPdeathsig intフィールドが追加されました。
  • src/pkg/syscall/Makefile:
    • 各OS向けのGOFILES_変数に、新しく追加されたexec_bsd.goまたはexec_linux.goが適切に組み込まれるように変更されました。
  • src/pkg/syscall/mkerrors.sh:
    • prctlシステムコールに関連する定数(PR_SET_PDEATHSIGなど)を自動生成するための定義が追加されました。
  • src/pkg/syscall/zerrors_linux_386.go, src/pkg/syscall/zerrors_linux_amd64.go, src/pkg/syscall/zerrors_linux_arm.go:
    • PR_で始まるprctl関連の定数(PR_SET_PDEATHSIG, PR_GET_PDEATHSIGなど)が追加されました。
  • src/buildscript_*.sh (各OS向けビルドスクリプト):
    • syscallパッケージのコンパイル時に、exec_unix.goの代わりにexec_bsd.goまたはexec_linux.goが使用されるように変更されました。

コアとなるコードの解説

このコミットの核心は、src/pkg/syscall/exec_linux.goに新しく追加されたforkAndExecInChild関数と、それに伴うSysProcAttr構造体の変更です。

src/pkg/syscall/exec_linux.go

// +build linux

package syscall

import (
	"unsafe"
)

type SysProcAttr struct {
	Chroot     string      // Chroot.
	Credential *Credential // Credential.
	Ptrace     bool        // Enable tracing.
	Setsid     bool        // Create session.
	Setpgid    bool        // Set process group ID to new pid (SYSV setpgrp)
	Setctty    bool        // Set controlling terminal to fd 0
	Noctty     bool        // Detach fd 0 from controlling terminal
	Pdeathsig  int         // Signal that the process will get when its parent dies (Linux only)
}

func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
	// ... (既存のforkロジック) ...

	// Fork succeeded, now in child.

	// Parent death signal
	if sys.Pdeathsig != 0 {
		_, _, err1 = RawSyscall6(SYS_PRCTL, PR_SET_PDEATHSIG, uintptr(sys.Pdeathsig), 0, 0, 0, 0)
		if err1 != 0 {
			goto childerror
		}

		// Signal self if parent is already dead. This might cause a
		// duplicate signal in rare cases, but it won't matter when
		// using SIGKILL.
		r1, _, _ = RawSyscall(SYS_GETPPID, 0, 0, 0)
		if r1 == 1 { // Parent is init (already dead or adopted)
			pid, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
			_, _, err1 := RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0)
			if err1 != 0 {
				goto childerror
			}
		}
	}

	// ... (既存のchroot, setuid/gid, chdir, fd操作, execveロジック) ...
}
  • SysProcAttr構造体:
    • Pdeathsig intフィールドが追加されました。このフィールドは、親プロセスが終了したときに子プロセスに送信されるシグナルの番号(例: syscall.SIGTERM)を保持します。Goのos/exec.Cmd構造体のSysProcAttrフィールドを通じて設定されることが想定されます。
  • forkAndExecInChild関数:
    • この関数は、fork()によって作成された子プロセス内で実行されます。
    • sys.Pdeathsig != 0の条件が追加され、Pdeathsigが設定されている場合にのみ、親プロセス死亡シグナルの設定ロジックが実行されます。
    • RawSyscall6(SYS_PRCTL, PR_SET_PDEATHSIG, uintptr(sys.Pdeathsig), 0, 0, 0, 0):
      • これはLinuxカーネルのprctlシステムコールを呼び出す部分です。
      • SYS_PRCTLprctlシステムコールの番号です。
      • PR_SET_PDEATHSIGは、prctlに渡すコマンドで、「親プロセス死亡シグナルを設定する」ことを意味します。
      • uintptr(sys.Pdeathsig)は、親プロセス死亡時に送信されるシグナル番号です。
    • RawSyscall(SYS_GETPPID, 0, 0, 0):
      • PR_SET_PDEATHSIGを設定した直後に、現在の親プロセスID(PPID)を取得します。
      • もしPPIDが1(initプロセス)であれば、それは元の親プロセスが既に終了していることを意味します。この場合、子プロセスは既に孤児となっているため、PR_SET_PDEATHSIGが設定されてもシグナルを受け取ることはありません。
      • このため、if r1 == 1の条件が真の場合、子プロセス自身にPdeathsigで指定されたシグナルを送信します(RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0))。これにより、親プロセスがPR_SET_PDEATHSIGを設定する前に終了してしまった場合でも、子プロセスが適切に終了するように促されます。

src/pkg/syscall/exec_bsd.go

このファイルは、Linux以外のUnix系OS向けのforkAndExecInChild関数を実装しており、PdeathsigフィールドやPR_SET_PDEATHSIGに関するロジックは含まれていません。これにより、Linux固有の機能が他のプラットフォームに影響を与えることなく、コードベースがクリーンに保たれています。

この変更により、Go言語で外部プロセスを起動する際に、親プロセスの終了に連動して子プロセスも終了させるという、より堅牢なプロセス管理が可能になりました。

関連リンク

参考にした情報源リンク

  • Go言語のsyscallパッケージのドキュメント (当時のバージョンに基づく)
  • Linux prctl(2) manページ
  • Unix/Linux プロセス管理に関する一般的な知識I have drafted the content based on the commit information and my understanding of the concepts. I will now output the complete Markdown.
# [インデックス 11052] ファイルの概要

このコミットは、Go言語の`syscall`パッケージにおいて、Linux固有の「親プロセス死亡シグナル (parent death signal)」のサポートを追加するものです。これにより、子プロセスが親プロセスの終了時に特定のシグナルを受け取るように設定できるようになり、プロセスのライフサイクル管理がより堅牢になります。

## コミット

*   **コミットハッシュ**: `2cb6fcf63f9f7f87ffa78b86a23bf33fc19e76ad`
*   **作者**: Albert Strasheim <fullung@gmail.com>
*   **コミット日時**: 2012年1月9日 月曜日 21:37:46 +0900

## GitHub上でのコミットページへのリンク

[https://github.com/golang/go/commit/2cb6fcf63f9f7f87ffa78b86a23bf33fc19e76ad](https://github.com/golang/go/commit/2cb6fcf63f9f7f87ffa78b86a23bf33fc19e76ad)

## 元コミット内容

syscall: Linux-only support for parent death signal

As discussed in this thread:

https://groups.google.com/group/golang-dev/browse_thread/thread/5b76b7700265a787

I've tried to come up with a solution that is minimally invasive for the platforms that don't support "parent death signal", without splitting up exec_unix.go.

See also: http://www.win.tue.nl/~aeb/linux/lk/lk-5.html#ss5.8

R=rsc, dave, borman, iant, mikioh.mikioh CC=golang-dev https://golang.org/cl/5487061


## 変更の背景

この変更の背景には、Go言語で起動された子プロセスが、その親プロセスが予期せず終了した場合に「孤児」となり、システムリソースを消費し続ける可能性があるという問題がありました。特に、親プロセスがクラッシュしたり、適切に子プロセスを終了させずに終了したりする場合に、ゾンビプロセスやデッドロックの原因となることが考えられます。

コミットメッセージで言及されているGoogle Groupsのスレッド(`https://groups.google.com/group/golang-dev/browse_thread/thread/5b76b7700265a787`)では、この問題と、Linuxカーネルが提供する`PR_SET_PDEATHSIG`という機能を使ってこれを解決する可能性について議論されています。`PR_SET_PDEATHSIG`は、親プロセスが終了した際に、子プロセスに特定のシグナル(例えば`SIGTERM`や`SIGKILL`)を送信するように設定できる機能です。これにより、親プロセスが終了した際に子プロセスも自動的に終了するように促すことができ、リソースリークを防ぎ、システムの健全性を保つことができます。

このコミットの目的は、`exec_unix.go`を分割することなく、親プロセス死亡シグナルをサポートしないプラットフォームへの影響を最小限に抑えつつ、Linux環境でのこの重要な機能を提供することでした。

## 前提知識の解説

### 1. プロセスと親子関係

Unix系OSでは、プロセスは階層構造を持っています。新しいプロセスは既存のプロセス(親プロセス)によって作成され、作成されたプロセスは子プロセスとなります。子プロセスは親プロセスから多くの属性(環境変数、開いているファイルディスクリプタなど)を継承します。

### 2. `fork()`と`exec()`システムコール

*   **`fork()`**: 既存のプロセス(親プロセス)のほぼ完全なコピーである新しいプロセス(子プロセス)を作成するシステムコールです。`fork()`が成功すると、親プロセスと子プロセスの両方で実行が継続されますが、戻り値が異なります(親プロセスには子プロセスのPID、子プロセスには0が返されます)。
*   **`exec()`ファミリー**: `exec`ファミリーのシステムコール(例: `execve`, `execl`, `execvp`など)は、現在のプロセスイメージを新しいプログラムで置き換えます。これにより、新しいプログラムが現在のプロセスのコンテキスト(PID、開いているファイルディスクリプタなど)で実行されます。通常、新しいプログラムを起動する際には、まず`fork()`で子プロセスを作成し、その子プロセス内で`exec()`を呼び出して新しいプログラムを実行します。

### 3. シグナル

シグナルは、Unix系OSにおけるプロセス間通信の一種で、特定のイベントが発生したことをプロセスに通知するソフトウェア割り込みのようなものです。例えば、`SIGTERM`はプロセスに終了を要求するシグナル、`SIGKILL`はプロセスを強制終了するシグナルです。

### 4. `prctl()`システムコールと`PR_SET_PDEATHSIG`

`prctl()`(process control)は、Linux固有のシステムコールで、呼び出し元プロセスの動作や属性を制御するために使用されます。様々な操作が可能ですが、このコミットで重要なのは`PR_SET_PDEATHSIG`オプションです。

*   **`PR_SET_PDEATHSIG`**: このオプションは、現在のプロセス(子プロセス)の親プロセスが終了したときに、指定されたシグナルを子プロセスに送信するように設定します。これにより、親プロセスが予期せず終了した場合でも、子プロセスが孤児として残り続けることを防ぎ、自動的に終了させることができます。

### 5. ファイルディスクリプタ (File Descriptor, FD)

ファイルディスクリプタは、Unix系OSにおいてファイルやI/Oリソース(ソケット、パイプなど)を識別するために使用される整数値です。プロセスが`fork()`されると、子プロセスは親プロセスの開いているファイルディスクリプタを継承します。

### 6. `CLOEXEC` (Close-on-exec) フラグ

ファイルディスクリプタには`CLOEXEC`というフラグを設定できます。このフラグが設定されているファイルディスクリプタは、`exec()`システムコールが成功した際に自動的に閉じられます。これは、子プロセスが不要なファイルディスクリプタを継承してしまわないようにするために重要です。

## 技術的詳細

このコミットの主要な技術的詳細は、Linuxカーネルが提供する`PR_SET_PDEATHSIG`機能のGo言語`syscall`パッケージへの統合です。

Go言語の`os/exec`パッケージは、内部的に`syscall`パッケージを利用して新しいプロセスを起動します。プロセス起動の核心は、`fork`と`exec`の組み合わせです。このコミット以前は、`exec_unix.go`というファイルがUnix系OS全般の`forkAndExecInChild`関数を実装していました。しかし、`PR_SET_PDEATHSIG`はLinux固有の機能であるため、既存の`exec_unix.go`を直接変更すると、他のUnix系OS(Darwin, FreeBSD, NetBSD, OpenBSDなど)に不要なコードが混入してしまいます。

この問題を解決するために、コミットでは以下の戦略が取られました。

1.  **プラットフォーム固有のファイル分割**:
    *   既存の`src/pkg/syscall/exec_unix.go`から、`forkAndExecInChild`関数の実装を削除しました。
    *   新しく`src/pkg/syscall/exec_bsd.go`と`src/pkg/syscall/exec_linux.go`というファイルが作成されました。
    *   `exec_bsd.go`は、Darwin, FreeBSD, NetBSD, OpenBSDなどのBSD系のOS向けに、従来の`exec_unix.go`にあった`forkAndExecInChild`の汎用的な実装を含みます。このファイルはビルドタグ`// +build darwin freebsd netbsd openbsd`によって、これらのOSでのみコンパイルされるように制御されます。
    *   `exec_linux.go`は、Linux向けに`forkAndExecInChild`のLinux固有の実装を含みます。このファイルはビルドタグ`// +build linux`によって、Linuxでのみコンパイルされるように制御されます。

2.  **`SysProcAttr`構造体の拡張**:
    *   `src/pkg/syscall/exec_linux.go`に定義される`SysProcAttr`構造体(プロセス起動時のシステム固有の属性を保持する)に、`Pdeathsig int`という新しいフィールドが追加されました。このフィールドは、親プロセスが終了した際に子プロセスに送信されるシグナル番号を指定します。`exec_bsd.go`にはこのフィールドは追加されず、Linux固有の機能であることを明確にしています。

3.  **`PR_SET_PDEATHSIG`の呼び出し**:
    *   `exec_linux.go`内の`forkAndExecInChild`関数内で、`sys.Pdeathsig`が0でない場合に`prctl(PR_SET_PDEATHSIG, sys.Pdeathsig)`システムコールが呼び出されるようになりました。これにより、子プロセスは親プロセスの終了時に指定されたシグナルを受け取るように設定されます。
    *   さらに、`PR_SET_PDEATHSIG`を設定した直後に、親プロセスが既に死亡している可能性を考慮して`GETPPID`で親のPIDを取得し、それが1(initプロセス)であれば、自身に`Pdeathsig`で指定されたシグナルを送信するロジックが追加されています。これは、親プロセスが`PR_SET_PDEATHSIG`を設定する前に終了してしまった場合のコーナーケースに対応するためです。

4.  **ビルドスクリプトとMakefileの更新**:
    *   各OS向けのビルドスクリプト(例: `src/buildscript_linux_386.sh`など)が更新され、`exec_unix.go`の代わりに、それぞれのOSに対応する`exec_bsd.go`または`exec_linux.go`がコンパイル対象に含まれるように変更されました。
    *   `src/pkg/syscall/Makefile`も更新され、`GOFILES_darwin`, `GOFILES_freebsd`, `GOFILES_linux`, `GOFILES_netbsd`, `GOFILES_openbsd`の各変数に、それぞれのプラットフォームでコンパイルされるべきファイルが適切にリストされるようになりました。特に、Linux関連の定義には`exec_linux.go`が、BSD関連の定義には`exec_bsd.go`が追加されています。

5.  **エラー定数の追加**:
    *   `src/pkg/syscall/mkerrors.sh`が更新され、`prctl`システムコールに関連する定数(`PR_SET_PDEATHSIG`など)がGoの`syscall`パッケージで利用できるように、`zerrors_linux_*.go`ファイルに自動生成されるようになりました。これにより、Goコードからこれらの定数を安全に参照できるようになります。

この変更により、GoアプリケーションはLinux上で子プロセスを起動する際に、親プロセスの死亡を検知し、適切に子プロセスを終了させるメカニズムを組み込むことができるようになりました。これは、長期実行されるサーバーアプリケーションや、複数のプロセスが連携して動作するシステムにおいて、リソース管理と安定性を向上させる上で非常に重要です。

## コアとなるコードの変更箇所

このコミットにおける主要なコード変更は以下のファイルに集中しています。

*   `src/pkg/syscall/exec_unix.go`:
    *   `forkAndExecInChild`関数の実装が削除されました。このファイルは、プラットフォーム固有の実装に置き換えられるための汎用的な定義のみを残す形になりました。
*   `src/pkg/syscall/exec_bsd.go` (新規作成):
    *   BSD系OS(Darwin, FreeBSD, NetBSD, OpenBSD)向けの`forkAndExecInChild`関数の実装が追加されました。これは、従来の`exec_unix.go`にあった汎用的なロジックを継承しています。
    *   `SysProcAttr`構造体が定義されていますが、`Pdeathsig`フィールドは含まれません。
*   `src/pkg/syscall/exec_linux.go` (新規作成):
    *   Linux向けの`forkAndExecInChild`関数の実装が追加されました。この実装には、`PR_SET_PDEATHSIG`システムコールを呼び出すロジックが含まれています。
    *   `SysProcAttr`構造体に`Pdeathsig int`フィールドが追加されました。
*   `src/pkg/syscall/Makefile`:
    *   各OS向けの`GOFILES_`変数に、新しく追加された`exec_bsd.go`または`exec_linux.go`が適切に組み込まれるように変更されました。
*   `src/pkg/syscall/mkerrors.sh`:
    *   `prctl`システムコールに関連する定数(`PR_SET_PDEATHSIG`など)を自動生成するための定義が追加されました。
*   `src/pkg/syscall/zerrors_linux_386.go`, `src/pkg/syscall/zerrors_linux_amd64.go`, `src/pkg/syscall/zerrors_linux_arm.go`:
    *   `PR_`で始まる`prctl`関連の定数(`PR_SET_PDEATHSIG`, `PR_GET_PDEATHSIG`など)が追加されました。
*   `src/buildscript_*.sh` (各OS向けビルドスクリプト):
    *   `syscall`パッケージのコンパイル時に、`exec_unix.go`の代わりに`exec_bsd.go`または`exec_linux.go`が使用されるように変更されました。

## コアとなるコードの解説

このコミットの核心は、`src/pkg/syscall/exec_linux.go`に新しく追加された`forkAndExecInChild`関数と、それに伴う`SysProcAttr`構造体の変更です。

### `src/pkg/syscall/exec_linux.go`

```go
// +build linux

package syscall

import (
	"unsafe"
)

type SysProcAttr struct {
	Chroot     string      // Chroot.
	Credential *Credential // Credential.
	Ptrace     bool        // Enable tracing.
	Setsid     bool        // Create session.
	Setpgid    bool        // Set process group ID to new pid (SYSV setpgrp)
	Setctty    bool        // Set controlling terminal to fd 0
	Noctty     bool        // Detach fd 0 from controlling terminal
	Pdeathsig  int         // Signal that the process will get when its parent dies (Linux only)
}

func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
	// ... (既存のforkロジック) ...

	// Fork succeeded, now in child.

	// Parent death signal
	if sys.Pdeathsig != 0 {
		_, _, err1 = RawSyscall6(SYS_PRCTL, PR_SET_PDEATHSIG, uintptr(sys.Pdeathsig), 0, 0, 0, 0)
		if err1 != 0 {
			goto childerror
		}

		// Signal self if parent is already dead. This might cause a
		// duplicate signal in rare cases, but it won't matter when
		// using SIGKILL.
		r1, _, _ = RawSyscall(SYS_GETPPID, 0, 0, 0)
		if r1 == 1 { // Parent is init (already dead or adopted)
			pid, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0)
			_, _, err1 := RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0)
			if err1 != 0 {
				goto childerror
			}
		}
	}

	// ... (既存のchroot, setuid/gid, chdir, fd操作, execveロジック) ...
}
  • SysProcAttr構造体:
    • Pdeathsig intフィールドが追加されました。このフィールドは、親プロセスが終了したときに子プロセスに送信されるシグナルの番号(例: syscall.SIGTERM)を保持します。Goのos/exec.Cmd構造体のSysProcAttrフィールドを通じて設定されることが想定されます。
  • forkAndExecInChild関数:
    • この関数は、fork()によって作成された子プロセス内で実行されます。
    • sys.Pdeathsig != 0の条件が追加され、Pdeathsigが設定されている場合にのみ、親プロセス死亡シグナルの設定ロジックが実行されます。
    • RawSyscall6(SYS_PRCTL, PR_SET_PDEATHSIG, uintptr(sys.Pdeathsig), 0, 0, 0, 0):
      • これはLinuxカーネルのprctlシステムコールを呼び出す部分です。
      • SYS_PRCTLprctlシステムコールの番号です。
      • PR_SET_PDEATHSIGは、prctlに渡すコマンドで、「親プロセス死亡シグナルを設定する」ことを意味します。
      • uintptr(sys.Pdeathsig)は、親プロセス死亡時に送信されるシグナル番号です。
    • RawSyscall(SYS_GETPPID, 0, 0, 0):
      • PR_SET_PDEATHSIGを設定した直後に、現在の親プロセスID(PPID)を取得します。
      • もしPPIDが1(initプロセス)であれば、それは元の親プロセスが既に終了していることを意味します。この場合、子プロセスは既に孤児となっているため、PR_SET_PDEATHSIGが設定されてもシグナルを受け取ることはありません。
      • このため、if r1 == 1の条件が真の場合、子プロセス自身にPdeathsigで指定されたシグナルを送信します(RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0))。これにより、親プロセスがPR_SET_PDEATHSIGを設定する前に終了してしまった場合でも、子プロセスが適切に終了するように促されます。

src/pkg/syscall/exec_bsd.go

このファイルは、Linux以外のUnix系OS向けのforkAndExecInChild関数を実装しており、PdeathsigフィールドやPR_SET_PDEATHSIGに関するロジックは含まれていません。これにより、Linux固有の機能が他のプラットフォームに影響を与えることなく、コードベースがクリーンに保たれています。

この変更により、Go言語で外部プロセスを起動する際に、親プロセスの終了に連動して子プロセスも終了させるという、より堅牢なプロセス管理が可能になりました。

関連リンク

参考にした情報源リンク

  • Go言語のsyscallパッケージのドキュメント (当時のバージョンに基づく)
  • Linux prctl(2) manページ
  • Unix/Linux プロセス管理に関する一般的な知識