[インデックス 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
は、親プロセスが終了した際に、子プロセスに特定のシグナル(例えば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など)に不要なコードが混入してしまいます。
この問題を解決するために、コミットでは以下の戦略が取られました。
-
プラットフォーム固有のファイル分割:
- 既存の
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でのみコンパイルされるように制御されます。
- 既存の
-
SysProcAttr
構造体の拡張:src/pkg/syscall/exec_linux.go
に定義されるSysProcAttr
構造体(プロセス起動時のシステム固有の属性を保持する)に、Pdeathsig int
という新しいフィールドが追加されました。このフィールドは、親プロセスが終了した際に子プロセスに送信されるシグナル番号を指定します。exec_bsd.go
にはこのフィールドは追加されず、Linux固有の機能であることを明確にしています。
-
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
を設定する前に終了してしまった場合のコーナーケースに対応するためです。
-
ビルドスクリプトと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
が追加されています。
- 各OS向けのビルドスクリプト(例:
-
エラー定数の追加:
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
フィールドは含まれません。
- BSD系OS(Darwin, FreeBSD, NetBSD, OpenBSD)向けの
src/pkg/syscall/exec_linux.go
(新規作成):- Linux向けの
forkAndExecInChild
関数の実装が追加されました。この実装には、PR_SET_PDEATHSIG
システムコールを呼び出すロジックが含まれています。 SysProcAttr
構造体にPdeathsig int
フィールドが追加されました。
- Linux向けの
src/pkg/syscall/Makefile
:- 各OS向けの
GOFILES_
変数に、新しく追加されたexec_bsd.go
またはexec_linux.go
が適切に組み込まれるように変更されました。
- 各OS向けの
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_PRCTL
はprctl
システムコールの番号です。PR_SET_PDEATHSIG
は、prctl
に渡すコマンドで、「親プロセス死亡シグナルを設定する」ことを意味します。uintptr(sys.Pdeathsig)
は、親プロセス死亡時に送信されるシグナル番号です。
- これはLinuxカーネルの
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言語で外部プロセスを起動する際に、親プロセスの終了に連動して子プロセスも終了させるという、より堅牢なプロセス管理が可能になりました。
関連リンク
- Google Groups スレッド: https://groups.google.com/group/golang-dev/browse_thread/thread/5b76b7700265a787
- 親プロセス死亡シグナルに関する議論が行われたスレッド。
- Linux Kernel Documentation (prctl): http://www.win.tue.nl/~aeb/linux/lk/lk-5.html#ss5.8
prctl
システムコール、特にPR_SET_PDEATHSIG
に関するLinuxカーネルのドキュメントの一部。
参考にした情報源リンク
- 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_PRCTL
はprctl
システムコールの番号です。PR_SET_PDEATHSIG
は、prctl
に渡すコマンドで、「親プロセス死亡シグナルを設定する」ことを意味します。uintptr(sys.Pdeathsig)
は、親プロセス死亡時に送信されるシグナル番号です。
- これはLinuxカーネルの
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言語で外部プロセスを起動する際に、親プロセスの終了に連動して子プロセスも終了させるという、より堅牢なプロセス管理が可能になりました。
関連リンク
- Google Groups スレッド: https://groups.google.com/group/golang-dev/browse_thread/thread/5b76b7700265a787
- 親プロセス死亡シグナルに関する議論が行われたスレッド。
- Linux Kernel Documentation (prctl): http://www.win.tue.nl/~aeb/linux/lk/lk-5.html#ss5.8
prctl
システムコール、特にPR_SET_PDEATHSIG
に関するLinuxカーネルのドキュメントの一部。
参考にした情報源リンク
- Go言語の
syscall
パッケージのドキュメント (当時のバージョンに基づく) - Linux
prctl(2)
manページ - Unix/Linux プロセス管理に関する一般的な知識