Golang exec process и отказаться от него
Я пытаюсь развить процессы с моим демоном и пытаюсь отречься от них в случае сбоя моего демона. Обычный os/exec
высокого уровня, поэтому я пошел к syscall.ForkExec
и произвел следующий код:
package main
import (
"fmt"
"os"
"os/exec"
"syscall"
"time"
)
func main() {
cmd := "myproc"
binary, lookErr := exec.LookPath(cmd)
if lookErr != nil {
panic(lookErr)
}
fmt.Println(binary)
os.Remove("/tmp/stdin")
os.Remove("/tmp/stdout")
os.Remove("/tmp/stderr")
fstdin, err1 := os.Create("/tmp/stdin")
fstdout, err2 := os.Create("/tmp/stdout")
fstderr, err3 := os.Create("/tmp/stderr")
if err1 != nil || err2 != nil || err3 != nil {
fmt.Println(err1, err2, err3)
panic("WOW")
}
argv := []string{"hi"}
procAttr := syscall.ProcAttr{
Dir: "/tmp",
Files: []uintptr{fstdin.Fd(), fstdout.Fd(), fstderr.Fd()},
Env: []string{"VAR1=ABC123"},
Sys: &syscall.SysProcAttr{
Foreground: false,
},
}
pid, err := syscall.ForkExec(binary, argv, &procAttr)
fmt.Println("Spawned proc", pid, err)
time.Sleep(time.Second * 100)
}
Я также сделал простое приложение, которое спит и печатает hello world и помещает его в путь.
#include <stdio.h>
int main(){
while(1){
printf("hello world");
fflush(stdout);
usleep(300000);
}
}
он работает, однако процесс не отправляется в фоновый режим, как я ожидал, мой процесс go все еще владеет ребенком. The SysProcAttr
имеет следующие значения в Linux:
type SysProcAttr struct {
Chroot string // Chroot.
Credential *Credential // Credential.
Ptrace bool // Enable tracing.
Setsid bool // Create session.
Setpgid bool // Set process group ID to Pgid, or, if Pgid == 0, to new pid.
Setctty bool // Set controlling terminal to fd Ctty (only meaningful if Setsid is set)
Noctty bool // Detach fd 0 from controlling terminal
Ctty int // Controlling TTY fd
Foreground bool // Place child's process group in foreground. (Implies Setpgid. Uses Ctty as fd of controlling TTY)
Pgid int // Child's process group ID if Setpgid.
Pdeathsig Signal // Signal that the process will get when its parent dies (Linux only)
Cloneflags uintptr // Flags for clone calls (Linux only)
UidMappings []SysProcIDMap // User ID mappings for user namespaces.
GidMappings []SysProcIDMap // Group ID mappings for user namespaces.
// GidMappingsEnableSetgroups enabling setgroups syscall.
// If false, then setgroups syscall will be disabled for the child process.
// This parameter is no-op if GidMappings == nil. Otherwise for unprivileged
// users this should be set to false for mappings work.
GidMappingsEnableSetgroups bool
}
Я также попробовал следующее, Но это вызвало ошибку:
Sys: &syscall.SysProcAttr{
Setsid: true,
Setctty: true,
Foreground: false,
},
Spawned proc 0 inappropriate ioctl for device
также следующее:
Sys: &syscall.SysProcAttr{
Setsid: true,
Setctty: true,
Foreground: false,
Noctty: true,
Setpgid: true,
},
Spawned proc 0 operation not permitted (with root privilleges)
что я делаю исходя из того, что не так? Примечание: несмотря на то, что os / exec является высоким уровнем, я также попробовал следующее, Но он дал те же результаты.
cs := exec.Command(binary)
cs.SysProcAttr = &syscall.SysProcAttr{
Setctty: true,
}
err := cs.Run()
fmt.Println(err)
4 ответов
процесс разветвленный с Start()
будет продолжаться даже после смерти родителя.
func forker() {
cmd := exec.Command("sleep", "3")
cmd.Start()
time.Sleep(2 * time.Second)
os.Exit(1)
}
на sleep
процесс будет счастливо жить в течение 3 секунд, даже если родительский процесс живет только в течение 2 секунд:
$ forker &; while true; do ps -f; sleep 1; done
UID PID PPID C STIME TTY TIME CMD
501 71423 69892 0 3:01PM ttys003 0:00.07 forker
501 71433 71432 0 3:01PM ttys003 0:00.00 sleep 3
UID PID PPID C STIME TTY TIME CMD
501 71423 69892 0 3:01PM ttys003 0:00.07 forker
501 71433 71432 0 3:01PM ttys003 0:00.00 sleep 3
UID PID PPID C STIME TTY TIME CMD
501 71433 1 0 3:01PM ttys003 0:00.00 sleep 3
обратите внимание, как идентификатор родительского процесса (PPID
) из
на Start() метод должно дать вам то, что вы ищете. Мой скрипт для добавления простого текстового файла после того, как моя программа Go завершила работу, продолжал работать:
package main
import (
"os/exec"
)
func main() {
cmd := exec.Command("./appender.sh")
cmd.Start()
}
кажется хорошей стратегией для немедленного отказа от нереста os.Argv[0]
process (self-spawn), затем нерестовый целевой процесс (с Setsid: true), а затем выход из первого порожденного процесса. Таким образом, процесс gradchild немедленно получает ppid 1. При необходимости первый порожденный процесс может передавать pid granchild через stdout родительскому перед выходом.
демонизация не играет хорошо с планированием goroutine go, поэтому детали становятся довольно грубыми. godaemon имеет небольшую реализацию с хорошей дискуссией и ссылками на более хорошую дискуссию, а также более популярную и более традиционно реализованную go-daemon.
на ioctl
ошибки, которые вы получаете почти наверняка приходят от использования Setctty
.
Sys: &syscall.SysProcAttr{
Foreground: false,
Setsid: true,
},
вам семантика как os/exec
' s Start
, где дочерний процесс не сразу перезаправляется, но будет безопасно поднят, если/когда родитель умрет.
The operation not permitted
вы видите, я думаю, о трубах в /tmp/
, от бега под strace:
openat(AT_FDCWD, "/tmp/stdin", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 3
epoll_create1(EPOLL_CLOEXEC) = 4
epoll_ctl(4, EPOLL_CTL_ADD, 3, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=471047936, u64=140179613654784}}) = -1 EPERM (Operation not permitted)
epoll_ctl(4, EPOLL_CTL_DEL, 3, 0xc420039c24) = -1 EPERM (Operation not permitted)
openat(AT_FDCWD, "/tmp/stdout", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 5
epoll_ctl(4, EPOLL_CTL_ADD, 5, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=471047936, u64=140179613654784}}) = -1 EPERM (Operation not permitted)
epoll_ctl(4, EPOLL_CTL_DEL, 5, 0xc420039c24) = -1 EPERM (Operation not permitted)
openat(AT_FDCWD, "/tmp/stderr", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 6
epoll_ctl(4, EPOLL_CTL_ADD, 6, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=471047936, u64=140179613654784}}) = -1 EPERM (Operation not permitted)
epoll_ctl(4, EPOLL_CTL_DEL, 6, 0xc420039c24) = -1 EPERM (Operation not permitted)