Maison  >  Article  >  développement back-end  >  Comment puis-je exécuter un processus de longue durée dans Golang et le détacher de mon programme tout en contrôlant l'utilisateur, en redirigeant la sortie et en l'empêchant de devenir un zombie ?

Comment puis-je exécuter un processus de longue durée dans Golang et le détacher de mon programme tout en contrôlant l'utilisateur, en redirigeant la sortie et en l'empêchant de devenir un zombie ?

Linda Hamilton
Linda Hamiltonoriginal
2024-10-28 04:17:30237parcourir

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?

Exécuter une commande dans Go et la détacher du processus

Problème :

Vous souhaitez exécuter un long- exécuter un processus dans Golang tout en répondant à plusieurs exigences :

  • Rediriger la sortie standard du processus vers un fichier
  • Contrôler l'utilisateur du processus
  • Empêcher le processus de mourir lorsque votre programme se termine
  • Évitez que le processus ne devienne un zombie
  • Obtenez le PID du processus en cours

Tentative de solution :

Vous avez tenté une solution en utilisant exec.Command mais elle ne répond pas à toutes les exigences, en particulier lorsque votre programme reçoit des signaux SIGTERM/SIGKILL.

Point clé à comprendre :

Il est important de noter que vous ne pouvez pas changer le parent d'un processus une fois qu'il a été démarré - la relation parent-enfant d'un processus est fixe.

Bibliothèques alternatives :

Au lieu de réinventer la roue, il est recommandé d'utiliser une bibliothèque existante qui résout ce problème. Les bibliothèques suivantes sont suggérées :

  • https://github.com/hashicorp/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/

Exemple avec 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>

Cet exemple utilise go-reap pour démarrer un shell (fonction Sh()) et exécuter une commande dans ce shell. Il configure le Reaper pour gérer le nettoyage des processus enfants.

En utilisant une bibliothèque réputée, vous pouvez éviter les pièges courants et vous assurer que votre application se comporte comme prévu.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn