Go でのプロセスのタイムアウトと子プロセスの終了
Go では、子プロセスをタイムアウトして終了する機能は、システムの安定性を維持し、アプリケーションの応答性を確保します。ただし、次のコードに示すように、プロセス終了時に不一致が発生する可能性があります。
func() { var output bytes.Buffer cmd := exec.Command("Command", args...) cmd.Dir = filepath.Dir(srcFile) cmd.Stdout, cmd.Stderr = &output, &output if err := cmd.Start(); err != nil { return err } defer time.AfterFunc(time.Second*2, func() { fmt.Printf("Nobody got time fo that\n") if err := cmd.Process.Signal(syscall.SIGKILL); err != nil { fmt.Printf("Error:%s\n", err) } fmt.Printf("It's dead Jim\n") }).Stop() err := cmd.Wait() fmt.Printf("Done waiting\n") }()
このコードでは、タイムアウト メカニズムに欠陥があるようです。「ジムは死んだ」と正しく出力されますが、タイムアウト メカニズムは失敗します。 「待機完了」を出力し、子プロセスを実行したままにします。
根本原因: SIGKILL制限事項
原因は、プロセスを終了するための cmd.Process.Signal(syscall.SIGKILL) の使用にあります。場合によっては、SIGKILL だけでは子プロセスを強制終了できない場合があります。根本的な理由は、最新のオペレーティング システムがプロセスごとに個別のプロセス グループを維持していることです。グループ内の 1 つのプロセスが SIGKILL を使用して別のグループ内の別のプロセスを終了しようとすると、シグナルが効果的に配信されない可能性があります。
解決策: 負のプロセス グループ ID を使用する
この問題を回避するには、負のプロセス グループ ID (-pgid) を syscall.Kill と組み合わせて使用することが解決策となります。プロセス グループ ID の前にマイナス記号を付けると、シグナルは個々のプロセスではなくプロセス グループ全体に送信されます。これにより、子プロセスも確実に終了します:
cmd := exec.Command( some_command ) cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} cmd.Start() pgid, err := syscall.Getpgid(cmd.Process.Pid) if err == nil { syscall.Kill(-pgid, 15) // note the minus sign } cmd.Wait()
注意事項とプラットフォームの考慮事項
このソリューションはすべてのオペレーティング システム間で移植できるわけではないことに注意することが重要です。 。プロセス グループを操作する機能は、プラットフォームによって異なります。このソリューションはテストされており、macOS Yosemite で動作しますが、Windows や一部の Linux などの他のプラットフォームでは動作が異なる場合があります。
以上がOS の不整合の可能性を考慮して、Go で子プロセスを確実にタイムアウトして終了するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。