英文:
Ensure executables called in Go Process get killed when Process is killed
问题
我有一些 Go 代码,其中有几个不同的线程在运行一个独立的可执行文件。我想确保如果用户在 Go 中终止了我的进程,我有一种方法来终止我调用的可执行文件,有办法做到吗?
英文:
I have some code that in Go (golang), has a few different threads running a separate executable. I want to ensure that if a user kills my process in Go, that I have a way of killing the executables I've called, is there a way to do that?
答案1
得分: 17
确保子进程被终止的唯一方法是在同一进程组中启动它,并将整个进程组一起终止,或者在syscall.SetProcAttr
中设置Pdeathsig
。
您可以为常见信号(如SIG_INT
和SIG_TERM
)设置信号处理程序,在退出之前终止子进程,但由于无法捕获SIG_KILL
,这通常不值得努力。
参考:https://stackoverflow.com/questions/34095254/panic-in-other-goroutine-not-stopping-child-process/34095869#34095869
cmd := exec.Command("./long-process")
cmd.SysProcAttr = &syscall.SysProcAttr{
Pdeathsig: syscall.SIGTERM,
}
英文:
The only ways to ensure that the child process is killed, is to start it in the same process group, and kill the process group as a whole, or set Pdeadthsig
in the syscall.SetProcAttr
.
You can setup a signal handler for common signals like SIG_INT
and SIG_TERM
, and kill your child processes before exiting, but since you can't catch SIG_KILL
that's often not worth the effort.
cmd := exec.Command("./long-process")
cmd.SysProcAttr = &syscall.SysProcAttr{
Pdeathsig: syscall.SIGTERM,
}
答案2
得分: 1
如果你使用的是Windows系统,你可能会发现以下代码片段很有帮助:
package main
import (
"os"
"os/exec"
"unsafe"
"golang.org/x/sys/windows"
)
// 我们使用这个结构体通过不安全的操作从os.Process中获取进程句柄(未导出的)
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
英文:
If you're on Windows, you might find this snippet helpful:
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)
}
}
Original link: https://gist.github.com/hallazzang/76f3970bfc949831808bbebc8ca15209
答案3
得分: 1
一种可能的策略是在一个全局数组var childProcesses = make([]*os.Process, 0)
中保持一个正在运行的进程列表,并在每次启动进程时将其添加到列表中。
拥有自己的Exit
函数。确保在代码中从不调用os.Exit
,而是始终调用自己的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)
})
英文:
One possible strategy is to keep a list of processes you're running in a global array var childProcesses = make([]*os.Process, 0)
and append to it every time you start a process.
Have your own Exit
function. Make sure that you never call os.Exit
anywhere in your code, and always call your own Exit
function instead. Your Exit function will kill all the childProcesses
for _, p := range childProcesses {
p.Kill()
}
Handle signals so they go through your own exit function, for example by doing this during initialization (somewhere near the top of your main function)
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)
})
答案4
得分: 0
我建议你使用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 {
}
}
英文:
I would suggest you to use unix.Prctl.
flag := unix.SIGHUP
if err := unix.Prctl(unix.PR_SET_PDEATHSIG, uintptr(flag), 0, 0, 0); err != nil {
return
}
A detailed example is below, new.go for child process and main.go as parent process.
//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 {
}
}
答案5
得分: 0
我有一些Go(golang)代码,其中有几个不同的线程在运行一个单独的可执行文件。我想确保如果用户在Go中终止我的进程,我有一种方法来终止我调用的可执行文件,有办法做到这一点吗?
我不太清楚你所说的"不同的线程运行"是什么意思。我会假设:
- 这些线程属于"我调用的可执行文件"
- 这些线程不一定是用golang编写的
- 你无法控制它们的执行(即,无法更改它们的代码)
在这种情况下,我们要创建一个进程组,并确保当你终止一个进程时,该组中的每个其他进程也必须停止。这个机制取决于操作系统。
在当前版本中,有两种可能性(参见x/sys包):
-
Unix:涉及到
setpgid(2)
-
Windows:涉及到
CreateProcess
。请参考MS Windows文档CreateProcess。你必须传递CREATE_NEW_PROCESS_GROUP
标志(参见https://learn.microsoft.com/en-us/windows/console/console-process-groups)。
英文:
> I have some code that in Go (golang), has a few different threads running a separate executable. I want to ensure that if a user kills my process in Go, that I have a way of killing the executables I've called, is there a way to do that?
It is not clear to me what you mean by "different threads running." I will assume:
- Those threads belong to "executables I've called"
- Those threads have not necessarily written in golang
- You cannot control their execution (i.e., you unable to change their code)
In this sense, we are talking about to create a process group and ensure that when you kill a process, every other process in that group must stop too. This mechanism is Operating System dependent.
In the current version, there are two possibilities (see package x/sys):
-
Unix: it is about to
setpgid(2)
-
Windows: it is about to
CreateProcess
. Please, refer to the MS Windows documentation CreateProcess. You must pass theCREATE_NEW_PROCESS_GROUP
flag (see)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论