У меня есть код, который в Go (golang) имеет несколько разных потоков, выполняющих отдельный исполняемый файл. Я хочу убедиться, что если пользователь убивает мой процесс в Go, у меня есть способ убить исполняемые файлы, которые я вызвал, есть ли способ сделать это?
Убедитесь, что исполняемые файлы, вызванные в процессе Go, будут убиты, когда процесс будет убит
Ответ 1
Единственный способ убедиться, что дочерний процесс завершен, - это запустить его в той же группе процессов и полностью уничтожить группу процессов или установить Pdeadthsig
в syscall.SetProcAddr
.
Вы можете настроить обработчик сигналов для общих сигналов, таких как SIG_INT
и SIG_TERM
, и убить ваши дочерние процессы перед выходом, но так как вы не можете поймать SIG_KILL
, который часто не стоит усилий.
Смотрите: Паника в другой программе не останавливает дочерний процесс
cmd := exec.Command("./long-process")
cmd.SysProcAttr = &syscall.SysProcAttr{
Pdeathsig: syscall.SIGTERM,
}
Ответ 2
Одной из возможных стратегий является сохранение списка процессов, которые вы выполняете, в глобальном массиве 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)
})
Ответ 3
Я предлагаю вам использовать 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 {
}
}
Ответ 4
Если вы работаете в 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
Ответ 5
У меня есть некоторый код, который в Go (golang), имеет несколько разных потоков, выполняющих отдельный исполняемый файл. Я хочу убедиться, что если пользователь убивает мой процесс в Go, у меня есть способ убить вызванные мной исполняемые файлы, есть ли способ сделать это?
Мне не понятно, что вы имеете в виду под "разными потоками". Я приму:
- Эти потоки принадлежат "исполняемым файлам, которые я назвал"
- Эти темы не обязательно написаны на Голанге
- Вы не можете контролировать их выполнение (т.е. вы не можете изменить их код)
В этом смысле мы говорим о том, чтобы создать группу процессов и обеспечить, чтобы при завершении процесса все остальные процессы в этой группе также останавливались. Этот механизм зависит от операционной системы.
В текущей версии есть две возможности (см. пакет x/sys):
Unix: он собирается
setgid(2)
Windows: скоро будет
CreateProcess
. Пожалуйста, обратитесь к документации MS Windows CreateProcess. Вы должны пройти флагCREATE_NEW_PROCESS_GROUP
(см.)