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

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

このコミットは、Go言語のosパッケージにおけるプロセス状態の報告メカニズムを、非ポータブルなWaitmsgから、よりポータブルで統一されたProcessStateに置き換えるものです。これにより、異なるオペレーティングシステム間でのプロセス管理の一貫性が向上し、プロセスの終了ステータス、CPU時間、リソース使用量などの情報へのアクセスが標準化されます。

コミット

osパッケージにおいて、プロセスの終了情報を扱うWaitmsg型を、よりポータブルなProcessState型に置き換える変更です。これにより、主要な情報へのアクセスはメソッドを通じて行われ、非ポータブルな部分はポータブルなメソッドを通じて提供されます。WindowsおよびPlan 9向けのコードも更新されています。

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

https://github.com/golang/go/commit/ccacab641af54f51bdca228445f464efde47e935

元コミット内容

commit ccacab641af54f51bdca228445f464efde47e935
Author: Rob Pike <r@golang.org>
Date:   Tue Feb 21 14:10:34 2012 +1100

    os: replace non-portable Waitmsg with portable ProcessState
    Use methods for key questions.
    Provide access to non-portable pieces through portable methods.
    Windows and Plan 9 updated.
    
    R=golang-dev, bradfitz, bradfitz, r, dsymonds, rsc, iant, iant
    CC=golang-dev
    https://golang.org/cl/5673077

変更の背景

Go言語の初期のバージョンでは、プロセスの終了情報を取得するためにos.Waitmsgという型が使用されていました。しかし、このWaitmsgはプラットフォームに依存する部分が多く、特にUnix系のシステムで使われるsyscall.WaitStatusを直接ラップする形であったため、WindowsやPlan 9といった異なるOSでの互換性に課題がありました。

Go 1のリリースに向けて、標準ライブラリのポータビリティと一貫性を高めることが重要な目標とされました。プロセス管理においても、各OSのシステムコールに直接依存するのではなく、より抽象化された共通のインターフェースを提供することで、開発者がプラットフォームの違いを意識せずにコードを書けるようにする必要がありました。

このコミットは、その目標を達成するための一環として、os.Waitmsgos.ProcessStateに置き換えるものです。ProcessStateは、プロセスの終了ステータス、CPU時間、リソース使用量などの情報を、OSに依存しない形で提供するための新しい抽象化レイヤーとして導入されました。これにより、Goプログラムが異なるOS上でより予測可能かつ一貫した動作をするようになります。

前提知識の解説

  • プロセス (Process): 実行中のプログラムのインスタンスです。各プロセスは独自のメモリ空間、ファイルディスクリプタ、実行コンテキストを持ちます。Go言語のosパッケージは、プロセスの生成、管理、終了の待機などの機能を提供します。
  • os.Process: Go言語で実行中のプロセスを表す型です。この型を通じて、プロセスのPID(プロセスID)を取得したり、プロセスにシグナルを送ったり、プロセスの終了を待機したりすることができます。
  • os.Process.Wait(): os.Process型のメソッドで、対象のプロセスが終了するまで待機し、その終了に関する情報を返します。このコミット以前はos.Waitmsgを返していましたが、コミット後は*os.ProcessStateを返します。
  • 終了ステータス (Exit Status): プロセスが終了する際に、その成否を示すために返す整数値です。慣例として、0は成功、0以外の値はエラーを示します。
  • シグナル (Signal): オペレーティングシステムがプロセスに送信する非同期通知です。例えば、SIGTERMはプロセスに終了を要求し、SIGKILLはプロセスを強制終了させます。
  • リソース使用量 (Resource Usage): プロセスが実行中に消費したCPU時間(ユーザー時間、システム時間)、メモリ、I/Oなどのシステムリソースの量です。Unix系システムではrusage構造体で表現されることが多いです。
  • syscall.WaitStatus (Unix/POSIX): Unix系システムでwaitpidなどのシステムコールが返す、プロセスの終了ステータスに関する詳細情報を含む型です。プロセスが正常終了したか、シグナルによって終了したか、停止したかなどの情報が含まれます。
  • syscall.Waitmsg (Plan 9): Plan 9オペレーティングシステムにおけるプロセスの終了情報を表す型です。
  • ポータビリティ (Portability): ソフトウェアが異なる環境(OS、ハードウェアアーキテクチャなど)で変更なしに、または最小限の変更で動作する能力を指します。

技術的詳細

このコミットの核心は、プロセスの終了情報を扱うための抽象化レイヤーをos.Waitmsgからos.ProcessStateへと変更した点にあります。

os.Waitmsgの問題点: 従来のos.Waitmsgは、Unix系システムではsyscall.WaitStatusを、Plan 9ではsyscall.Waitmsgを直接ラップする形でした。これは、各OSのシステムコールが返す生の情報に近いため、プラットフォーム間でインターフェースが異なり、コードのポータビリティを損ねていました。例えば、UnixのWaitStatusにはシグナル情報やコアダンプの有無が含まれますが、Plan 9のWaitmsgは異なる構造をしていました。

os.ProcessStateの導入: os.ProcessStateは、これらのプラットフォーム固有の差異を吸収し、統一されたインターフェースを提供する新しい型として設計されました。

ProcessStateは以下の主要なメソッドを提供します。

  • Pid() int: 終了したプロセスのプロセスIDを返します。
  • Exited() bool: プログラムが正常に終了したかどうかを報告します。
  • Success() bool: プログラムが成功裏に終了したかどうか(Unix系では終了ステータス0)を報告します。
  • Sys() interface{}: システム依存の終了情報を返します。これはinterface{}型であるため、呼び出し側は適切な型アサーション(例: *syscall.WaitStatus*syscall.Waitmsg)を行うことで、基盤となるOS固有の情報にアクセスできます。
  • SysUsage() interface{}: システム依存のリソース使用量情報を返します。これもinterface{}型であり、*syscall.Rusageなどの型アサーションが必要です。
  • UserTime() time.Duration: 終了したプロセスとその子プロセスのユーザーCPU時間を返します。
  • SystemTime() time.Duration: 終了したプロセスとその子プロセスのシステムCPU時間を返します。

これらのメソッドにより、開発者はOSの違いを意識することなく、プロセスの終了情報を取得できるようになります。必要に応じて、Sys()SysUsage()を通じて低レベルのOS固有の情報にアクセスすることも可能です。

プラットフォームごとの実装:

  • exec_plan9.go: Plan 9では、syscall.WaitmsgProcessStateの内部フィールドstatusとして保持し、Pid()Exited()Success()などのメソッドがこのstatusフィールドを利用して情報を提供します。UserTime()SystemTime()syscall.WaitmsgTimeフィールドからミリ秒単位で取得します。
  • exec_posix.go: POSIX準拠のシステム(Unix系)では、syscall.WaitStatusProcessStateの内部フィールドstatusとしてポインタで保持し、syscall.Rusagerusageフィールドとして保持します。Pid()Exited()Success()などのメソッドはstatusフィールドを利用します。UserTime()SystemTime()syscall.Rusageからナノ秒単位で取得します。
  • exec_unix.go: exec_posix.goと同様に、syscall.Wait4システムコールを使用してsyscall.WaitStatussyscall.Rusageを取得し、これらをProcessStateに格納します。
  • exec_windows.go: Windowsでは、syscall.WaitForSingleObjectGetExitCodeProcessを使用してプロセスの終了を待機し、終了コードを取得します。UserTime()SystemTime()はWindowsでは常に0を返します(当時の実装ではCPU時間のリソース使用量取得がサポートされていなかったため)。

このように、ProcessStateは各OSの特性に合わせて内部実装を切り替えつつ、外部には統一されたインターフェースを提供するという、Go言語のポータビリティ戦略を体現しています。

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

このコミットでは、主に以下のファイルが変更されています。

  1. src/cmd/cgo/util.go:
    • p.Wait()の戻り値がw, err := p.Wait()からstate, err := p.Wait()に変更され、w.Exited() && w.ExitStatus() == 0state.Success()に置き換えられています。
  2. src/cmd/godoc/main.go:
    • wait.ExitStatus()wait.Exited()の代わりにwait.Success()が使用されるよう変更されています。
    • コメントでWaitがブール値の終了条件を持つようになったことが言及されています。
  3. src/pkg/os/exec/exec.go:
    • Cmd構造体のWaitmsgフィールドがProcessStateフィールドに変更されています。
    • ExitError構造体が*os.Waitmsgを埋め込む形から*os.ProcessStateを埋め込む形に変更されています。
    • Cmd.Wait()メソッド内でc.Process.Wait()の戻り値がmsgからstateに変更され、c.Waitmsg = msgc.ProcessState = stateに、!msg.Exited() || msg.ExitStatus() != 0!state.Success()に、&ExitError{msg}&ExitError{state}にそれぞれ変更されています。
  4. src/pkg/os/exec_plan9.go:
    • Waitmsg型が削除され、ProcessState型が新しく定義されています。
    • ProcessState型にはpidstatussyscall.Waitmsg型)フィールドが含まれ、Pid()Exited()Success()Sys()SysUsage()UserTime()SystemTime()String()メソッドが実装されています。
    • Process.Wait()の戻り値が*Waitmsgから*ProcessStateに変更され、ProcessStateのインスタンスを生成して返すように修正されています。
  5. src/pkg/os/exec_posix.go:
    • Waitmsg型が削除され、ProcessState型が新しく定義されています。
    • ProcessState型にはpidstatus*syscall.WaitStatus型)、rusage*syscall.Rusage型)フィールドが含まれ、Pid()Exited()Success()Sys()SysUsage()メソッドが実装されています。
    • ProcessState.String()メソッドが、WaitStatusの情報を利用してより詳細な終了理由を文字列で返すように変更されています。
  6. src/pkg/os/exec_unix.go:
    • Process.Wait()の戻り値が*Waitmsgから*ProcessStateに変更され、syscall.Wait4で取得したsyscall.WaitStatussyscall.Rusageを基にProcessStateのインスタンスを生成して返すように修正されています。
    • ProcessStateUserTime()SystemTime()メソッドが追加され、rusageからCPU時間を取得するように実装されています。
  7. src/pkg/os/exec_windows.go:
    • Process.Wait()の戻り値が*Waitmsgから*ProcessStateに変更され、ProcessStateのインスタンスを生成して返すように修正されています。
    • ProcessStateUserTime()SystemTime()メソッドが追加されていますが、Windowsでは常に0を返すように実装されています。
  8. src/pkg/os/os_test.go:
    • TestNilWaitmsgStringテストがTestNilProcessStateStringにリネームされ、*Waitmsgの代わりに*ProcessStateString()メソッドのテストを行うように修正されています。

コアとなるコードの解説

  • src/cmd/cgo/util.gosrc/cmd/godoc/main.go の変更: これらのファイルは、os.Process.Wait()の呼び出し元であり、Waitmsgの代わりにProcessStateを使用するように更新されています。特に注目すべきは、w.Exited() && w.ExitStatus() == 0のような複数の条件チェックが、新しいstate.Success()という単一のメソッド呼び出しに置き換えられている点です。これは、ProcessStateが提供する高レベルな抽象化と、より意図が明確なAPI設計の恩恵を示しています。

  • src/pkg/os/exec/exec.go の変更: このファイルは、os/execパッケージの主要なロジックを含んでいます。Cmd構造体内のWaitmsgフィールドがProcessStateに置き換えられたことで、execパッケージ全体が新しいProcessStateの抽象化を利用するようになります。ExitErrorも同様にProcessStateをラップすることで、エラー報告の一貫性が保たれます。Cmd.Wait()メソッドは、os.Process.Wait()から返されるProcessStateを受け取り、それをCmd構造体のフィールドに格納し、必要に応じてExitErrorを生成します。これにより、execパッケージのユーザーは、プロセスの終了情報をProcessStateを通じて取得できるようになります。

  • src/pkg/os/exec_plan9.go, src/pkg/os/exec_posix.go, src/pkg/os/exec_unix.go, src/pkg/os/exec_windows.go の変更: これらのファイルは、各オペレーティングシステムに特化したos.Process.Wait()の実装を含んでいます。

    • 各ファイルで、従来のWaitmsg型が削除され、新しいProcessState型が定義されています。このProcessState型は、各OSのシステムコール(例: Unix/POSIXのsyscall.Wait4、Plan 9のsyscall.Waitmsg、Windowsのsyscall.WaitForSingleObject)から取得した生の情報(syscall.WaitStatussyscall.Rusageなど)を内部に保持します。
    • ProcessState型には、Pid()Exited()Success()Sys()SysUsage()UserTime()SystemTime()String()といった、OSに依存しない共通のメソッドが実装されています。これらのメソッドは、内部に保持するOS固有の情報を適切に解釈し、共通の形式で返します。
    • 例えば、Unix/POSIXではUserTime()SystemTime()syscall.Rusageから正確なCPU時間を取得するのに対し、Windowsでは当時の実装の制約から0を返すように実装されています。これは、ProcessStateが提供するポータブルなインターフェースの下で、各OSの機能の差異を吸収している典型的な例です。
    • Process.Wait()メソッドは、各OSのシステムコールを呼び出してプロセスの終了情報を取得し、その情報を基にProcessStateのインスタンスを構築して返します。

これらの変更により、Goのosパッケージは、プロセスの終了情報を扱うための堅牢でポータブルなメカニズムを提供できるようになりました。

関連リンク

参考にした情報源リンク