首页 > 后端开发 > Golang > 正文

如何在 Golang 中执行长时间运行的进程并将其与我的程序分离,同时控制用户、重定向输出并防止其成为僵尸进程?

Linda Hamilton
发布: 2024-10-28 04:17:30
原创
245 人浏览过

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/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/

使用 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 启动 shell(Sh() 函数)并在该 shell 中执行命令。它设置收割机来处理子进程的清理。

通过使用信誉良好的库,您可以避免常见的陷阱并确保您的应用程序按预期运行。

以上是如何在 Golang 中执行长时间运行的进程并将其与我的程序分离,同时控制用户、重定向输出并防止其成为僵尸进程?的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:php.cn
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责声明 Sitemap
PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!