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

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

このコミットは、Go言語の標準ライブラリos/execパッケージにおけるCmd構造体にWaitmsgフィールドを追加し、プロセス終了時の詳細な情報を取得できるようにする変更です。また、既存のコメントの誤解を招く表現を修正しています。

コミット

commit 5d198bf8661be6ad0659793eebaec0ab587eb876
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Fri Feb 10 14:52:08 2012 +1100

    os/exec: add Cmd.Waitmsg, fix a misleading comment
    
    Fixes #2948
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/5655048

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

https://github.com/golang/go/commit/5d198bf8661be6ad0659793eebaec0ab587eb876

元コミット内容

os/execパッケージにおいて、Cmd構造体にWaitmsgフィールドを追加し、誤解を招くコメントを修正しました。

この変更はIssue #2948を修正します。

変更の背景

このコミットの主な目的は、Goプログラムが外部プロセスを実行し、その終了を待つ際に、より詳細な終了ステータス情報を取得できるようにすることです。従来のCmd.Wait()メソッドは、プロセスの終了を待ち、エラーが発生した場合はそれを返しますが、プロセスの終了コードやシグナルに関する詳細な情報(例えば、プロセスが正常終了したのか、特定のシグナルによって終了したのかなど)を直接提供しませんでした。

Issue #2948("os/exec: Cmd.Wait should return os.Waitmsg")は、この情報不足を指摘し、Cmd.Wait()os.Waitmsgを返すようにするか、またはCmd構造体内にその情報を保持するフィールドを追加することを提案していました。このコミットは後者のアプローチを採用し、Cmd構造体にWaitmsgフィールドを追加することで、Wait()メソッドが呼び出された後にこの情報にアクセスできるようにしています。

また、既存のコメント「BUG: on OS X 10.6, child processes may sometimes inherit extra fds.」が「BUG: on OS X 10.6, child processes may sometimes inherit unwanted fds.」に修正されています。これは、extraという言葉が「追加の」という意味合いで、意図的に追加されたファイルディスクリプタと誤解される可能性があったため、「望ましくない」という意味のunwantedに修正することで、より正確な表現に改められたものです。これは、子プロセスが親プロセスから予期せぬファイルディスクリプタを継承してしまうという、OS X 10.6における既知のバグ(Issue #2603)に関するコメントの明確化です。

前提知識の解説

Go言語のos/execパッケージ

os/execパッケージは、外部コマンドを実行するための機能を提供します。これにより、Goプログラムからシェルコマンドや他の実行可能ファイルを起動し、その入出力を制御したり、終了を待ったりすることができます。

  • exec.Command(name string, arg ...string) *Cmd: 外部コマンドを表すCmd構造体を生成します。
  • Cmd.Run() error: コマンドを実行し、その終了を待ちます。成功した場合はnilを返します。
  • Cmd.Start() error: コマンドを非同期で実行します。
  • Cmd.Wait() error: Start()で開始されたコマンドの終了を待ちます。

os.Processos.Waitmsg

  • os.Process: 実行中の外部プロセスを表す構造体です。Cmd.Start()が成功すると、Cmd.Processフィールドにこのインスタンスが設定されます。
  • os.Waitmsg: プロセスが終了した際に、その終了に関する詳細な情報(終了ステータス、シグナル情報など)を保持する構造体です。これはUnix系のシステムコールであるwaitidwaitpidなどから返される情報に対応します。os.Process.Wait()メソッドは、このos.Waitmsgとエラーを返します。

ファイルディスクリプタ (File Descriptors, FDs)

ファイルディスクリプタは、Unix系OSにおいて、ファイルやソケット、パイプなどのI/Oリソースを識別するために使用される整数値です。プロセスは、親プロセスからファイルディスクリプタを継承することがあります。意図しないファイルディスクリプタの継承は、セキュリティ上の問題やリソースリークを引き起こす可能性があります。

技術的詳細

このコミットの技術的な核心は、os/exec.Cmd構造体にWaitmsg *os.Waitmsgフィールドを追加し、Cmd.Wait()メソッド内でos.Process.Wait()から返されるos.Waitmsgをこの新しいフィールドに格納することです。

従来のCmd.Wait()は、内部的にc.Process.Wait(0)を呼び出し、その結果として得られるos.Waitmsgとエラーを受け取っていました。しかし、このos.Waitmsgはメソッドの外部には公開されず、単にエラーの有無によってプロセスの成功/失敗を判断するのみでした。

この変更により、Cmd.Wait()またはCmd.Run()(内部でWait()を呼び出す)が完了した後、開発者はCmd.Waitmsgフィールドを通じて、プロセスの終了に関するより詳細な情報にアクセスできるようになります。例えば、プロセスが特定のシグナルによって終了したのか、または特定の終了コードで終了したのかなどをプログラムで確認できるようになります。これは、外部プロセスの挙動をより細かく制御・分析する必要があるアプリケーションにとって非常に有用です。

例えば、以下のような情報がos.Waitmsgから取得可能になります(具体的なフィールドはOSによって異なりますが、一般的には以下のような情報が含まれます):

  • ExitStatus(): プロセスの終了コード。
  • Signaled(): プロセスがシグナルによって終了したかどうか。
  • Signal(): プロセスを終了させたシグナル。
  • CoreDump(): コアダンプが生成されたかどうか。

コメントの修正は、技術的な機能変更ではありませんが、ドキュメントの正確性を向上させる重要な変更です。extra fdsという表現は、意図的に追加されたファイルディスクリプタと誤解される可能性がありましたが、unwanted fdsとすることで、OSのバグによって予期せず継承されるファイルディスクリプタを指すことが明確になりました。

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

変更はsrc/pkg/os/exec/exec.goファイルに対して行われています。

  1. コメントの修正:

    --- a/src/pkg/os/exec/exec.go
    +++ b/src/pkg/os/exec/exec.go
    @@ -68,7 +68,7 @@ type Cmd struct {
     	// new process. It does not include standard input, standard output, or
     	// standard error. If non-nil, entry i becomes file descriptor 3+i.
     	//
    -	// BUG: on OS X 10.6, child processes may sometimes inherit extra fds.
    +	// BUG: on OS X 10.6, child processes may sometimes inherit unwanted fds.
     	// http://golang.org/issue/2603
     	ExtraFiles []*os.File
    

    ExtraFilesフィールドに関するコメントのextraunwantedに修正されました。

  2. Cmd構造体へのWaitmsgフィールドの追加:

    --- a/src/pkg/os/exec/exec.go
    +++ b/src/pkg/os/exec/exec.go
    @@ -79,6 +79,10 @@ type Cmd struct {
      	// Process is the underlying process, once started.
      	Process *os.Process
      
    +	// Waitmsg contains information about an exited process,
    +	// available after a call to Wait or Run.
    +	Waitmsg *os.Waitmsg
    +
      	err             error // last error (from LookPath, stdin, stdout, stderr)
      	finished        bool  // when Wait was called
      	childFiles      []*os.File
    

    Cmd構造体にWaitmsg *os.Waitmsgフィールドが追加されました。

  3. Cmd.Wait()メソッド内でのWaitmsgの格納:

    --- a/src/pkg/os/exec/exec.go
    +++ b/src/pkg/os/exec/exec.go
    @@ -288,6 +292,7 @@ func (c *Cmd) Wait() error {
      	}\n \tc.finished = true
      	msg, err := c.Process.Wait(0)
    +\tc.Waitmsg = msg
      
      	var copyError error
      	for _ = range c.goroutine {
    

    Cmd.Wait()メソッド内で、c.Process.Wait(0)から返されたmsgos.Waitmsg型)が、新しく追加されたc.Waitmsgフィールドに代入されるようになりました。

コアとなるコードの解説

コメントの修正

ExtraFilesフィールドのコメントは、OS X 10.6における既知のバグ(Issue #2603)について言及しています。このバグは、子プロセスが親プロセスから予期せぬファイルディスクリプタを継承してしまうというものです。元のコメントの「extra fds」という表現は、「追加のファイルディスクリプタ」と解釈され、あたかも意図的に追加されたかのように誤解される可能性がありました。これを「unwanted fds」(望ましくないファイルディスクリプタ)に修正することで、このバグが予期せぬ、望ましくない継承であることを明確にしています。これにより、ドキュメントの正確性が向上し、開発者がこのバグについてより正確に理解できるようになります。

Cmd構造体へのWaitmsgフィールドの追加

type Cmd struct {
    // ... 既存のフィールド ...
    // Process is the underlying process, once started.
    Process *os.Process

    // Waitmsg contains information about an exited process,
    // available after a call to Wait or Run.
    Waitmsg *os.Waitmsg

    // ... その他のフィールド ...
}

この変更により、Cmd構造体は、実行された外部プロセスの終了に関する詳細な情報(os.Waitmsg)を保持できるようになりました。Waitmsgフィールドはポインタ型(*os.Waitmsg)であるため、プロセスがまだ終了していない場合や、Wait()またはRun()が呼び出されていない場合はnilになります。これにより、開発者はCmdインスタンスを通じて、プロセスの終了ステータス、シグナル情報、コアダンプの有無など、より豊富な情報にアクセスできるようになります。

Cmd.Wait()メソッド内でのWaitmsgの格納

func (c *Cmd) Wait() error {
    // ... 既存の処理 ...
    c.finished = true
    msg, err := c.Process.Wait(0)
    c.Waitmsg = msg // ここでWaitmsgを格納
    // ... 既存の処理 ...
    return err
}

Cmd.Wait()メソッドは、外部プロセスの終了を待ち、その結果を返します。このメソッドの内部で、c.Process.Wait(0)が呼び出され、プロセスの終了に関するos.Waitmsgとエラーが返されます。このコミットの重要な変更は、このmsgos.Waitmsg)が、新しく追加されたc.Waitmsgフィールドに代入されるようになった点です。

これにより、Cmd.Wait()が正常に完了した後(またはCmd.Run()が完了した後)、開発者はCmdインスタンスのWaitmsgフィールドを検査することで、プロセスの終了に関する詳細な情報を取得できます。例えば、以下のように利用できます。

package main

import (
	"fmt"
	"os"
	"os/exec"
)

func main() {
	cmd := exec.Command("sh", "-c", "exit 42") // 終了コード42で終了するコマンド
	err := cmd.Run()
	if err != nil {
		if exitError, ok := err.(*exec.ExitError); ok {
			fmt.Printf("Command finished with error: %v\n", exitError)
			if cmd.Waitmsg != nil {
				fmt.Printf("Waitmsg ExitStatus: %d\n", cmd.Waitmsg.ExitStatus())
			}
		} else {
			fmt.Printf("Error running command: %v\n", err)
		}
	} else {
		fmt.Printf("Command finished successfully.\n")
		if cmd.Waitmsg != nil {
			fmt.Printf("Waitmsg ExitStatus: %d\n", cmd.Waitmsg.ExitStatus())
		}
	}

	fmt.Println("---")

	cmd = exec.Command("sh", "-c", "kill -9 $$") // SIGKILLで終了するコマンド
	err = cmd.Run()
	if err != nil {
		if exitError, ok := err.(*exec.ExitError); ok {
			fmt.Printf("Command finished with error: %v\n", exitError)
			if cmd.Waitmsg != nil {
				fmt.Printf("Waitmsg Signaled: %t, Signal: %v\n", cmd.Waitmsg.Signaled(), cmd.Waitmsg.Signal())
			}
		} else {
			fmt.Printf("Error running command: %v\n", err)
		}
	}
}

この変更は、Go言語で外部プロセスを扱う際の柔軟性と情報量を大幅に向上させ、より堅牢なアプリケーション開発を可能にします。

関連リンク

参考にした情報源リンク