确保Go Process中调用的可执行文件在进程被杀死时被杀死

rjee0c15  于 2023-09-28  发布在  Go
关注(0)|答案(5)|浏览(119)

我有一些代码,在Go(golang)中,有几个不同的线程运行一个单独的可执行文件。我想确保如果用户在Go中杀死我的进程,我有办法杀死我调用的可执行程序,有办法做到吗?

s2j5cfk0

s2j5cfk01#

确保子进程被杀死的唯一方法是在同一个进程组中启动它,并将进程组作为一个整体杀死,或者在syscall.SetProcAttr中设置Pdeadthsig
您可以为常见的信号(如SIG_INTSIG_TERM)设置一个信号处理程序,并在退出前杀死子进程,但由于您无法捕获SIG_KILL,因此通常不值得这样做。
参见:其他goroutine中的Panic没有停止子进程

cmd := exec.Command("./long-process")

cmd.SysProcAttr = &syscall.SysProcAttr{
    Pdeathsig: syscall.SIGTERM,
}
fwzugrvs

fwzugrvs2#

如果你在Windows上,你可能会发现这个片段很有帮助:

package main

import (
    "os"
    "os/exec"
    "unsafe"

    "golang.org/x/sys/windows"
)

// We use this struct to retreive process handle(which is unexported)
// from os.Process using unsafe operation.
type process struct {
    Pid    int
    Handle uintptr
}

type ProcessExitGroup windows.Handle

func NewProcessExitGroup() (ProcessExitGroup, error) {
    handle, err := windows.CreateJobObject(nil, nil)
    if err != nil {
        return 0, err
    }

    info := windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION{
        BasicLimitInformation: windows.JOBOBJECT_BASIC_LIMIT_INFORMATION{
            LimitFlags: windows.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
        },
    }
    if _, err := windows.SetInformationJobObject(
        handle,
        windows.JobObjectExtendedLimitInformation,
        uintptr(unsafe.Pointer(&info)),
        uint32(unsafe.Sizeof(info))); err != nil {
        return 0, err
    }

    return ProcessExitGroup(handle), nil
}

func (g ProcessExitGroup) Dispose() error {
    return windows.CloseHandle(windows.Handle(g))
}

func (g ProcessExitGroup) AddProcess(p *os.Process) error {
    return windows.AssignProcessToJobObject(
        windows.Handle(g),
        windows.Handle((*process)(unsafe.Pointer(p)).Handle))
}

func main() {
    g, err := NewProcessExitGroup()
    if err != nil {
        panic(err)
    }
    defer g.Dispose()

    cmd := exec.Command("notepad.exe", "noname")
    if err := cmd.Start(); err != nil {
        panic(err)
    }

    if err := g.AddProcess(cmd.Process); err != nil {
        panic(err)
    }

    if err := cmd.Wait(); err != nil {
        panic(err)
    }
}

原文链接:https://gist.github.com/hallazzang/76f3970bfc949831808bbebc8ca15209

lf5gs5x2

lf5gs5x23#

一种可能的策略是将正在运行的进程列表保存在一个全局数组var childProcesses = make([]*os.Process, 0)中,并在每次启动一个进程时添加到该数组中。
拥有自己的Exit功能。请确保在代码中的任何地方都不要调用os.Exit,而是始终调用自己的Exit函数。您的退出函数将杀死所有的childProcesses

for _, p := range childProcesses {
    p.Kill()
}

处理信号,使它们通过您自己的退出函数,例如在初始化期间这样做(在主函数顶部附近)

sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL, syscall.SIGQUIT)
goUnsafe(func() {
    var signal = <-sigs
    log.Println("Got Signal", signal)
    Exit(0)
})
qvtsj1bj

qvtsj1bj4#

建议使用unix.prctl。

flag := unix.SIGHUP
if err := unix.Prctl(unix.PR_SET_PDEATHSIG, uintptr(flag), 0, 0, 0); err != nil {
    return
}

下面是一个详细的例子,new.go作为子进程,main.go作为父进程。

//new.go
package main

func main() {
    flag := unix.SIGHUP

    if err := unix.Prctl(unix.PR_SET_PDEATHSIG, uintptr(flag), 0, 0, 0); err != nil {
    return
    }

    f, _ := os.Create("./dat2")
    defer f.Close()
    i := 0
    for {
        n3, _ := f.WriteString("writes " + string(i) + "\n")
        fmt.Printf("wrote %d bytes\n", n3)
        f.Sync()
        i += 2
        time.Sleep(2 * time.Second)
    }

    for {
        n3, _ := f.WriteString("newwrites\n")
        fmt.Printf("wrote %d bytes\n", n3)
        f.Sync()
        time.Sleep(2 * time.Second)
    }
}

//main.go
package main

import "os/exec"

func main() {
    commandA := exec.Command("./new")
    commandA.Start()
    commandB := exec.Command("./new")
    commandB.Start()
    for {
    }
}
5lwkijsr

5lwkijsr5#

我有一些代码,在Go(golang)中,有几个不同的线程运行一个单独的可执行文件。我想确保如果用户在Go中杀死我的进程,我有办法杀死我调用的可执行程序,有办法做到吗?
我不清楚你所说的“不同的线程运行”是什么意思。我假设:

  • 那些线程属于“我调用过的可执行文件”
  • 这些线程不一定是用golang写的
  • 您无法控制它们的执行(即,您无法更改它们的代码)

在这个意义上,我们正在讨论创建一个进程组,并确保当您杀死一个进程时,该组中的所有其他进程也必须停止。此机制取决于操作系统。
在当前版本中,有两种可能性(请参见软件包x/sys):

*Unix:即将setpgid(2)
*Windows:大约是CreateProcess。请参阅MS Windows documentation CreateProcess。必须传递CREATE_NEW_PROCESS_GROUP标志(see

相关问题