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

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

このコミットは、Go言語の標準ライブラリosパッケージにおけるプロセス管理関連のドキュメンテーションと実装の整理を目的としています。具体的には、ProcessおよびProcessState構造体の公開APIとそのドキュメンテーションをsrc/pkg/os/doc.goに一元化し、各プラットフォーム固有の実装ファイル(exec_plan9.go, exec_posix.go, exec_unix.go, exec_windows.go)からそれらの公開定義を削除し、内部関数として変更しています。

変更されたファイルは以下の通りです。

  • src/pkg/os/doc.go: ProcessおよびProcessStateの公開メソッドの定義とドキュメンテーションが追加されました。
  • src/pkg/os/exec_plan9.go: StartProcess, Signal, Kill, Wait, Exited, Success, Sys, SysUsage, UserTime, SystemTimeメソッドが非公開化されました。
  • src/pkg/os/exec_posix.go: StartProcess, Kill, Exited, Success, Sys, SysUsageメソッドが非公開化されました。
  • src/pkg/os/exec_unix.go: Wait, Signal, UserTime, SystemTimeメソッドが非公開化されました。
  • src/pkg/os/exec_windows.go: Wait, Signal, UserTime, SystemTimeメソッドが非公開化されました。

コミット

commit 30db6d41cdaa65bb2d192853313f61cc7f12a39d
Author: Russ Cox <rsc@golang.org>
Date:   Thu Mar 1 21:56:54 2012 -0500

    os: centralize documentation of Process and ProcessState
    
    Also change Wait to say "exit" instead of "exit or stop".
    
    I notice that Pid is not implemented on all systems.
    Should we fix that?
    
    Fixes #3138.
    
    R=golang-dev, alex.brainman, r
    CC=golang-dev
    https://golang.org/cl/5710056

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

https://github.com/golang/go/commit/30db6d41cdaa65bb2d192853313f61cc7f12a39d

元コミット内容

os: centralize documentation of Process and ProcessState

Also change Wait to say "exit" instead of "exit or stop".

I notice that Pid is not implemented on all systems.
Should we fix that?

Fixes #3138.

R=golang-dev, alex.brainman, r
CC=golang-dev
https://golang.org/cl/5710056

変更の背景

このコミットの主な背景は、Go言語のosパッケージにおけるProcessおよびProcessState型に関連するドキュメンテーションとAPI定義の整合性を高めることです。Goの標準ライブラリでは、公開されるAPIのドキュメンテーションは、そのAPIが定義されているファイルに記述されるのが一般的です。しかし、osパッケージのプロセス関連の機能は、Unix、Windows、Plan 9など、複数のオペレーティングシステム(OS)に特化したファイル(exec_unix.go, exec_windows.go, exec_plan9.goなど)に分散して実装されていました。

これにより、以下のような問題が生じていました。

  1. ドキュメンテーションの重複と不整合: 各OS固有のファイルで同じ公開メソッドのドキュメンテーションが重複して記述される可能性があり、更新漏れや不整合が生じやすくなります。
  2. API定義の分散: ProcessProcessStateの公開メソッドのシグネチャが複数のファイルに散らばっているため、パッケージ全体のAPIを把握しにくくなります。
  3. Waitメソッドのドキュメンテーションの修正: Waitメソッドの既存のドキュメンテーションが「exit or stop」と記述されていましたが、これはプロセスの終了状態を正確に反映していない可能性がありました。GoのosパッケージのWaitは、通常、プロセスが終了した状態を待つものであり、「停止」状態(例えばUnixのSIGSTOPによる停止)を直接扱うものではありません。このため、より正確な「exit」という表現に修正する必要がありました。

このコミットは、これらの問題を解決するために、公開APIの定義とドキュメンテーションをdoc.goという単一のファイルに集約し、各OS固有の実装ファイルではそれらのメソッドを非公開のヘルパー関数として扱うように変更しています。これにより、ドキュメンテーションの一元管理とAPI定義の明確化が図られます。

また、コミットメッセージにFixes #3138.とあることから、この変更がGoのIssue 3138に関連していることがわかります。このIssueは、osパッケージのProcessProcessStateのドキュメンテーションが分散していることによる問題提起であったと推測されます。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびOSに関する基本的な知識が必要です。

  1. Go言語のパッケージと公開/非公開識別子:

    • Go言語では、パッケージ内の識別子(変数、関数、型、メソッドなど)の先頭が大文字で始まる場合、その識別子はパッケージ外からアクセス可能な「公開(exported)」識別子となります。
    • 先頭が小文字で始まる場合、その識別子はパッケージ内でのみアクセス可能な「非公開(unexported)」識別子となります。
    • このルールは、APIの設計において非常に重要であり、外部に公開するインターフェースと内部実装を明確に区別するために用いられます。
  2. osパッケージ:

    • Goの標準ライブラリosパッケージは、オペレーティングシステムとの基本的な相互作用を提供します。これには、ファイルシステム操作、プロセス管理、環境変数へのアクセスなどが含まれます。
    • 特にプロセス管理においては、os.Process型が実行中のプロセスを表し、os.ProcessState型が終了したプロセスの状態情報(終了コード、CPU時間など)を表します。
  3. os.Processos.ProcessState:

    • os.Processは、os.StartProcessos.FindProcessによって取得される、実行中のプロセスへの参照です。この型には、プロセスを操作するためのメソッド(例: Kill, Signal, Wait)が含まれます。
    • os.ProcessStateは、os.Process.Waitメソッドが返す構造体で、終了したプロセスの詳細な情報を提供します。これには、プロセスの終了ステータス、CPU使用時間などが含まれます。
  4. クロスプラットフォーム開発:

    • Go言語はクロスプラットフォームを強く意識して設計されており、多くの標準ライブラリは異なるOS(Linux, Windows, macOS, Plan 9など)で動作するように実装されています。
    • osパッケージのようにOS固有の機能にアクセスする場合、Goのビルドシステムは、特定のOS向けに書かれたファイルを自動的に選択してコンパイルします。例えば、ファイル名が_unix.goで終わるファイルはUnix系OS(Linux, macOSなど)でのみコンパイルされ、_windows.goで終わるファイルはWindowsでのみコンパイルされます。これにより、OS固有のシステムコールやAPIを透過的に利用できます。
  5. doc.goファイル:

    • Goのパッケージでは、doc.goという名前のファイルが特別な意味を持つことがあります。このファイルは、パッケージ全体のドキュメンテーションや、パッケージ内の主要な型や関数の公開APIドキュメンテーションを記述するために使用されることがあります。これにより、パッケージの概要や重要なAPIの情報を一箇所に集約し、godocツールで生成されるドキュメントの品質を向上させることができます。

技術的詳細

このコミットの技術的な核心は、Go言語の公開/非公開識別子のルールと、doc.goファイルの慣習を巧みに利用して、osパッケージのプロセス管理APIの構造を改善した点にあります。

変更前は、os.Processos.ProcessStateのメソッド(例: Wait, Kill, Signal, UserTimeなど)は、各OS固有の実装ファイル(exec_unix.go, exec_windows.go, exec_plan9.go, exec_posix.go)内で直接、公開メソッドとして定義されていました。例えば、exec_unix.goにはfunc (p *Process) Wait() (*ProcessState, error)のような定義があり、その上にGodocコメントが付与されていました。

このコミットでは、以下の手順でリファクタリングが行われました。

  1. doc.goへの公開APIの移動と一元化:

    • src/pkg/os/doc.goファイルに、os.Processおよびos.ProcessStateのすべての公開メソッドのシグネチャと、それに対応するGodocコメントが追加されました。
    • これらのメソッドは、実際の実装を呼び出すための薄いラッパーとして機能します。例えば、func (p *Process) Wait() (*ProcessState, error) { return p.wait() }のように、内部の非公開メソッドを呼び出す形になります。
    • 同様に、os.StartProcessのような公開関数もdoc.goに移動され、内部のstartProcess関数を呼び出す形になりました。
  2. 各OS固有ファイルでのメソッドの非公開化:

    • exec_plan9.go, exec_posix.go, exec_unix.go, exec_windows.goの各ファイルでは、これまで公開されていたProcessおよびProcessStateのメソッドが、対応する非公開のメソッド(例: wait, kill, signal, userTimeなど)に名前が変更されました。
    • これにより、これらのファイルはもはや公開APIを直接定義するのではなく、doc.goで定義された公開APIから呼び出される内部的な実装の詳細を担うことになります。
  3. Waitメソッドのドキュメンテーション修正:

    • doc.goに移動されたWaitメソッドのドキュメンテーションにおいて、「exit or stop」という表現が「exit」に修正されました。これは、Goのos.Process.Waitがプロセスの終了(exit)を待つことを明確にするための変更です。Unix系OSにおけるwaitpidシステムコールのように、プロセスが停止状態(stopped)になることを待つ機能は、osパッケージのWaitメソッドの主要な責務ではありません。

この変更により、osパッケージのプロセス管理APIの構造はよりクリーンで保守しやすくなりました。

  • 単一の真実の源 (Single Source of Truth): ProcessProcessStateの公開APIの定義とドキュメンテーションがdoc.goに集約されたことで、開発者はこのファイルを見るだけで、これらの型の提供する機能と使い方を完全に把握できるようになりました。これにより、ドキュメンテーションの重複や不整合が解消されます。
  • 実装の詳細の隠蔽: 各OS固有の実装ファイルは、公開APIの定義から切り離され、内部的な詳細を扱うようになりました。これは、Goの「インターフェースと実装の分離」という設計原則に沿ったものです。
  • コードの可読性と保守性の向上: APIの定義と実装が明確に分離されたことで、コードベース全体の可読性が向上し、将来的な機能追加やバグ修正が容易になります。

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

このコミットのコアとなる変更は、src/pkg/os/doc.goへの大量の追加と、他のexec_*.goファイルからの対応するメソッドの削除および非公開化です。

src/pkg/os/doc.go の変更

このファイルに、ProcessProcessStateの公開メソッドのシグネチャとGodocコメントが追加されました。

--- a/src/pkg/os/doc.go
+++ b/src/pkg/os/doc.go
@@ -4,6 +4,8 @@
 
 package os
 
+import "time"
+
 // FindProcess looks for a running process by its pid.
 // The Process it returns can be used to obtain information
 // about the underlying operating system process.
@@ -11,6 +13,17 @@ func FindProcess(pid int) (p *Process, err error) {
 	return findProcess(pid)
 }
 
+// StartProcess starts a new process with the program, arguments and attributes
+// specified by name, argv and attr.
+//
+// StartProcess is a low-level interface. The os/exec package provides
+// higher-level interfaces.
+//
+// If there is an error, it will be of type *PathError.
+func StartProcess(name string, argv []string, attr *ProcAttr) (*Process, error) {
+	return startProcess(name, argv, attr)
+}
+
 // Release releases any resources associated with the Process p,
 // rendering it unusable in the future.
 // Release only needs to be called if Wait is not.
@@ -18,6 +31,58 @@ func (p *Process) Release() error {
 	return p.release()
 }
 
+// Kill causes the Process to exit immediately.
+func (p *Process) Kill() error {
+	return p.kill()
+}
+
+// Wait waits for the Process to exit, and then returns a
+// ProcessState describing its status and an error, if any.
+// Wait releases any resources associated with the Process.
+func (p *Process) Wait() (*ProcessState, error) {
+	return p.wait()
+}
+
+// Signal sends a signal to the Process.
+func (p *Process) Signal(sig Signal) error {
+	return p.signal(sig)
+}
+
+// UserTime returns the user CPU time of the exited process and its children.
+func (p *ProcessState) UserTime() time.Duration {
+	return p.userTime()
+}
+
+// SystemTime returns the system CPU time of the exited process and its children.
+func (p *ProcessState) SystemTime() time.Duration {
+	return p.systemTime()
+}
+
+// Exited returns whether the program has exited.
+func (p *ProcessState) Exited() bool {
+	return p.exited()
+}
+
+// Success reports whether the program exited successfully,
+// such as with exit status 0 on Unix.
+func (p *ProcessState) Success() bool {
+	return p.success()
+}
+
+// Sys returns system-dependent exit information about
+// the process.  Convert it to the appropriate underlying
+// type, such as syscall.WaitStatus on Unix, to access its contents.
+func (p *ProcessState) Sys() interface{} {
+	return p.sys()
+}
+
+// SysUsage returns system-dependent resource usage information about
+// the exited process.  Convert it to the appropriate underlying
+// type, such as *syscall.Rusage on Unix, to access its contents.
+func (p *ProcessState) SysUsage() interface{} {
+	return p.sysUsage()
+}
+
 // Hostname returns the host name reported by the kernel.
 func Hostname() (name string, err error) {
 	return hostname()

各OS固有ファイル (exec_plan9.go, exec_posix.go, exec_unix.go, exec_windows.go) の変更

これらのファイルでは、対応する公開メソッドが非公開メソッドにリネームされ、その上にあったGodocコメントが削除されました。

例: src/pkg/os/exec_plan9.go

--- a/src/pkg/os/exec_plan9.go
+++ b/src/pkg/os/exec_plan9.go
@@ -11,10 +11,7 @@ import (
 	"time"
 )
 
-// StartProcess starts a new process with the program, arguments and attributes
-// specified by name, argv and attr.
-// If there is an error, it will be of type *PathError.\n-func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
+func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
 	sysattr := &syscall.ProcAttr{
 		Dir: attr.Dir,
 		Env: attr.Env,
@@ -40,7 +37,7 @@ func (note Plan9Note) String() string {
 	return string(note)
 }
 
-func (p *Process) Signal(sig Signal) error {
+func (p *Process) signal(sig Signal) error {
 	if p.done {
 		return errors.New("os: process already finished")
 	}
@@ -54,8 +51,7 @@ func (p *Process) Signal(sig Signal) error {
 	return e
 }
 
-// Kill causes the Process to exit immediately.\n-func (p *Process) Kill() error {
+func (p *Process) kill() error {
 	f, e := OpenFile("/proc/"+itoa(p.Pid)+"/ctl", O_WRONLY, 0)
 	if e != nil {
 		return NewSyscallError("kill", e)
@@ -65,9 +61,7 @@ func (p *Process) Kill() error {
 	return e
 }
 
-// Wait waits for the Process to exit or stop, and then returns a
-// ProcessState describing its status and an error, if any.\n-func (p *Process) Wait() (ps *ProcessState, err error) {
+func (p *Process) wait() (ps *ProcessState, err error) {
 	var waitmsg syscall.Waitmsg
 
 	if p.Pid == -1 {
@@ -118,40 +112,27 @@ func (p *ProcessState) Pid() int {
 	return p.pid
 }
 
-// Exited returns whether the program has exited.\n-func (p *ProcessState) Exited() bool {
+func (p *ProcessState) exited() bool {
 	return p.status.Exited()
 }
 
-// Success reports whether the program exited successfully,\n-// such as with exit status 0 on Unix.\n-func (p *ProcessState) Success() bool {
+func (p *ProcessState) success() bool {
 	return p.status.ExitStatus() == 0
 }
 
-// Sys returns system-dependent exit information about
-// the process.  Convert it to the appropriate underlying
-// type, such as *syscall.Waitmsg on Plan 9, to access its contents.\n-func (p *ProcessState) Sys() interface{} {
+func (p *ProcessState) sys() interface{} {
 	return p.status
 }
 
-// SysUsage returns system-dependent resource usage information about
-// the exited process.  Convert it to the appropriate underlying
-// type, such as *syscall.Waitmsg on Plan 9, to access its contents.\n-func (p *ProcessState) SysUsage() interface{} {
+func (p *ProcessState) sysUsage() interface{} {
 	return p.status
 }
 
-// UserTime returns the user CPU time of the exited process and its children.\n-// It is always reported as 0 on Windows.\n-func (p *ProcessState) UserTime() time.Duration {
+func (p *ProcessState) userTime() time.Duration {
 	return time.Duration(p.status.Time[0]) * time.Millisecond
 }
 
-// SystemTime returns the system CPU time of the exited process and its children.\n-// It is always reported as 0 on Windows.\n-func (p *ProcessState) SystemTime() time.Duration {
+func (p *ProcessState) systemTime() time.Duration {
 	return time.Duration(p.status.Time[1]) * time.Millisecond
 }

コアとなるコードの解説

このコミットにおけるコアとなるコードの変更は、Go言語の「公開/非公開」の原則と、doc.goファイルの役割を最大限に活用したものです。

  1. APIの「ファサード」としてのdoc.go:

    • src/pkg/os/doc.goは、osパッケージのプロセス管理機能における公開APIの「ファサード(Facade)」として機能します。ファサードとは、複雑なサブシステムに対する統一されたインターフェースを提供するデザインパターンです。
    • このファイルには、os.Processos.ProcessStateのすべての公開メソッド(例: p.Wait(), p.Kill(), ps.UserTime()など)のシグネチャと、それらの機能に関する公式なドキュメンテーション(Godocコメント)が記述されています。
    • これらの公開メソッドの実装は非常にシンプルで、対応する非公開のヘルパー関数(例: p.wait(), p.kill(), ps.userTime()など)を呼び出すだけです。
  2. プラットフォーム固有の実装の非公開化:

    • 各OS固有のファイル(exec_plan9.go, exec_posix.go, exec_unix.go, exec_windows.go)では、これまで公開メソッドとして定義されていたものが、すべて非公開のヘルパー関数にリネームされました。
    • 例えば、func (p *Process) Wait()func (p *Process) wait()に、func (p *ProcessState) UserTime()func (p *ProcessState) userTime()に変更されています。
    • これにより、これらのファイルは、osパッケージの外部からは直接アクセスできない内部実装の詳細をカプセル化する役割を担うことになります。
  3. ビルド時の挙動:

    • Goのビルドシステムは、ターゲットOSに基づいて適切なexec_*.goファイルを選択してコンパイルします。例えば、Linux向けにビルドする場合、exec_unix.goがコンパイルされ、exec_windows.goexec_plan9.goは無視されます。
    • doc.goは常にコンパイルされるため、os.Process.Wait()のような公開メソッドの定義は常に存在します。この公開メソッドは、コンパイルされたOS固有の非公開メソッド(例: Linuxの場合はp.wait() in exec_unix.go)を呼び出すことで、実際の処理を実行します。

この設計は、Go言語の標準ライブラリでよく見られるパターンであり、以下の利点をもたらします。

  • APIの安定性: 公開APIの定義がdoc.goに一元化されることで、将来的にOS固有の実装が変更されたとしても、公開APIのシグネチャやドキュメンテーションに影響を与えることなく、内部の実装を修正できるようになります。
  • ドキュメンテーションの正確性: すべての公開APIのドキュメンテーションがdoc.goに集約されるため、ドキュメントの更新漏れや不整合のリスクが大幅に減少します。開発者は、doc.goだけを更新すれば、パッケージ全体のドキュメントが最新の状態に保たれることを保証できます。
  • コードの分離とモジュール性: 公開APIと内部実装が明確に分離されることで、コードベースのモジュール性が向上し、各ファイルの責務がより明確になります。これにより、大規模なプロジェクトでの共同開発や長期的なメンテナンスが容易になります。

関連リンク

参考にした情報源リンク