とある実行プロセス内で実行した別プロセスを親プロセスが死んだ段階で子プロセスも終了させたいというユースケースは結構あるかと思います。
Linuxではプロセスグループというものがあって、そのグループに属しているプロセスは親プロセスが死んだら子プロセスも一緒に終了してくれます。
Windowsにはプロセスグループに相当するものとしてジョブオブジェクトというものがあります。
同じジョブに属しているプロセスは親プロセスが死んだら一緒に子プロセスも死んでくれますが、プログラムでジョブにプロセスを登録してあげる必要があります。
C#での登録の仕方は以下のサイトに記述があります。今回はこれをGoで書いてみようと思います。
Goで書いてみると以下のような感じになりました。以下のコードの例では notepad を3つ立ち上げて 3秒後に終了するという単純なプログラムになっています。
大まかな流れとしては以下のような感じです。
CreateJobObject
でジョブを作成JOBOBJECT_BASIC_LIMIT_INFORMATION
構造体のLimitFlags
にJOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
を設定したJOBOBJECT_EXTENDED_LIMIT_INFORMATION
構造体を作成SetInformationJobObject
を作成した ジョブとJOBOBJECT_EXTENDED_LIMIT_INFORMATION
構造体で実行AssignProcessToJobObject
に 子プロセスのプロセスを渡して登録する
package main import ( "os" "os/exec" "time" "unsafe" "golang.org/x/sys/windows" ) type JobObject struct { Handle windows.Handle } func CreateAsKillOnJobClose() (*JobObject, error) { job, err := windows.CreateJobObject(nil, nil) if err != nil { return nil, err } info := windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION{ BasicLimitInformation: windows.JOBOBJECT_BASIC_LIMIT_INFORMATION{ LimitFlags: windows.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, }, } _, err = windows.SetInformationJobObject( job, windows.JobObjectExtendedLimitInformation, uintptr(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info))) if err != nil { panic(err) } return &JobObject{job}, nil } func (obj *JobObject) CloseHandle() error { return windows.CloseHandle(obj.Handle) } func (obj *JobObject) AssignProcess(p *os.Process) error { type process struct { Pid int Handle uintptr } if err := windows.AssignProcessToJobObject( obj.Handle, windows.Handle((*process)(unsafe.Pointer(p)).Handle)); err != nil { return err } return nil } func main() { cmds := make([]*exec.Cmd, 0) for i := 0; i < 3; i++ { cmd := exec.Command("notepad.exe") if err := cmd.Start(); err != nil { panic(err) } cmds = append(cmds, cmd) } jobObj, err := CreateAsKillOnJobClose() if err != nil { panic(err) } defer jobObj.CloseHandle() for _, cmd := range cmds { jobObj.AssignProcess(cmd.Process) } time.Sleep(3 * time.Second) }
懸念
上記の記事にも書かれているように親プロセスが終了すると子プロセスは強制終了させられるとのことで、ちゃんと終了したい場合は WM_CLOSE メッセージを送らないといけないかもとのことでした。
私のコード例ではそのあたりの処理が抜けているので、ちゃんとする場合はメッセージを送るような処理を入れる必要があるんでしょうね・・・