사용자를 제어하고 출력을 리디렉션하며 좀비가 되는 것을 방지하면서 Golang에서 장기 실행 프로세스를 실행하고 이를 내 프로그램에서 분리하려면 어떻게 해야 합니까?

Linda Hamilton
풀어 주다: 2024-10-28 04:17:30
원래의
337명이 탐색했습니다.

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을 사용하여 쉘(Sh() 함수)을 시작하고 해당 쉘 내에서 명령을 실행합니다. 하위 프로세스 정리를 처리하도록 리퍼를 설정합니다.

평판이 좋은 라이브러리를 사용하면 일반적인 함정을 피하고 애플리케이션이 의도한 대로 작동하는지 확인할 수 있습니다.

위 내용은 사용자를 제어하고 출력을 리디렉션하며 좀비가 되는 것을 방지하면서 Golang에서 장기 실행 프로세스를 실행하고 이를 내 프로그램에서 분리하려면 어떻게 해야 합니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
저자별 최신 기사
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿