ホームページ  >  記事  >  バックエンド開発  >  Golang で長時間実行プロセスを実行し、ユーザーを制御し、出力をリダイレクトし、ゾンビになるのを防ぎながら、それをプログラムから切り離すにはどうすればよいでしょうか?

Golang で長時間実行プロセスを実行し、ユーザーを制御し、出力をリダイレクトし、ゾンビになるのを防ぎながら、それをプログラムから切り離すにはどうすればよいでしょうか?

Linda Hamilton
Linda Hamiltonオリジナル
2024-10-28 04:17:30237ブラウズ

How can I execute a long-running process in Golang and detach it from my program while controlling the user, redirecting output, and preventing it from becoming a zombie?

Go でコマンドを実行し、プロセスから切り離す

問題:

長いコマンドを実行したいと考えています。いくつかの要件を満たしながら Golang でプロセスを実行する:

  • プロセスの標準出力をファイルにリダイレクトする
  • プロセスのユーザーを制御する
  • 次のような場合にプロセスが終了しないようにするプログラムが終了します
  • プロセスがゾンビになるのを回避します
  • 実行中のプロセスの PID を取得します

試行された解決策:

exec.Command を使用して解決策を試みましたが、特にプログラムが SIGTERM/SIGKILL シグナルを受信する場合、すべての要件を満たしていません。

理解すべき重要なポイント:

プロセスの親は、開始後に変更できないことに注意することが重要です。プロセスの親子関係は固定されています。

代替ライブラリ:

車輪を再発明する代わりに、この問題を解決する既存のライブラリを使用することをお勧めします。次のライブラリが推奨されます:

  • https://github.com/bashicorp/go-reap
  • https://github.com/krallin/tini
  • https://busybox.net/
  • https://software.clapper.org/daemonize/
  • https://wiki.gentoo.org/wiki/OpenRC
  • https://www.freedesktop.org/wiki/Software/systemd/

go-reap の例:

<code class="go">import (
    "fmt"
    "os"
    "os/exec"
    "strings"
    "sync"
    "time"

    "github.com/fatih/color"
    "github.com/hashicorp/go-reap"
)

func main() {

    if reap.IsSupported() {
        done := make(chan struct{})
        var reapLock sync.RWMutex
        pids := make(reap.PidCh, 1)

        errors := make(reap.ErrorCh, 1)
        go reap.ReapChildren(pids, errors, done, &reapLock)
        go report(pids, errors, done)

        // Here is where you would start your long-running process
        Sh()

        close(done)
    } else {
        fmt.Println("Sorry, go-reap isn't supported on your platform.")
    }
}

func report(pids reap.PidCh, errors reap.ErrorCh, done chan struct{}) {

    sprintf := color.New(color.FgWhite, color.Bold).SprintfFunc()

    for ;; {
        select {
        case pid := <-pids:
            println(sprintf(&quot;raeper pid %d&quot;, pid))
        case err := <-errors:
            println(sprintf(&quot;raeper er %s&quot;, err))
        case <-done:
            return
        }
    }
}

func Sh() {

    args := os.Args[1:]
    script := args[0:0]
    if len(args) >= 1 {
        if args[0] == &quot;-c&quot; {
            script = args[1:]
        }
    }
    if len(script) == 0 {
        fn.CyanBold(&quot;cmd: expecting sh -c 'foobar'&quot;)
        os.Exit(111)
    }

    var cmd *exec.Cmd
    parts, _ := shlex.Split(strings.Join(script, &quot; &quot;))
    if len(parts) >= 2 {
        cmd = fn.Merge(exec.Command(parts[0], parts[1:]...), nil)
    }
    if len(parts) == 1 {
        cmd = fn.Merge(exec.Command(parts[0]), nil)
    }

    // ... Here you can customize how the process is started and controlled

    if fn.IfEnv(&quot;HANG&quot;) {
        fn.CyanBold(&quot;cmd: %v\n      start&quot;, parts)
        ex := cmd.Start()
        if ex != nil {
            fn.CyanBold(&quot;cmd %v err: %s&quot;, parts, ex)
        }
        go func() {
            time.Sleep(time.Millisecond * 100)
            errw := cmd.Wait()
            if errw != nil {
                fn.CyanBold(&quot;cmd %v err: %s&quot;, parts, errw)
            } else {
                fn.CyanBold(&quot;cmd %v all done.&quot;, parts)
            }
        }()

        fn.CyanBold(&quot;cmd: %v\n      dispatched, hanging forever (i.e. to keep docker running)&quot;, parts)
        for {
            time.Sleep(time.Millisecond * time.Duration(fn.EnvInt(&quot;HANG&quot;, 2888)))
            fn.SystemCyan(&quot;/bin/ps&quot;, &quot;-e&quot;, &quot;-o&quot;, &quot;stat,comm,user,etime,pid,ppid&quot;)
        }

    } else {

        if fn.IfEnv(&quot;NOWAIT&quot;) {
            ex := cmd.Start()
            if ex != nil {
                fn.CyanBold(&quot;cmd %v start err: %s&quot;, parts, ex)
            }
        } else {

            ex := cmd.Run()
            if ex != nil {
                fn.CyanBold(&quot;cmd %v run err: %s&quot;, parts, ex)
            }
        }
        fn.CyanBold(&quot;cmd %v\n      dispatched, exit docker.&quot;, parts)
    }
}</code>

この例go-reap を使用してシェル (Sh() 関数) を開始し、そのシェル内でコマンドを実行します。子プロセスのクリーンアップを処理するようにリーパーを設定します。

評判の良いライブラリを使用することで、よくある落とし穴を回避し、アプリケーションが意図したとおりに動作することを保証できます。

以上がGolang で長時間実行プロセスを実行し、ユーザーを制御し、出力をリダイレクトし、ゾンビになるのを防ぎながら、それをプログラムから切り離すにはどうすればよいでしょうか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。