[英]Golang exec process and to disown it
I am trying to fork processes with my daemon, and trying to disown them in case of my daemon crashes. 我正在尝试使用我的守护进程分叉进程,并在我的守护程序崩溃的情况下尝试拒绝它们。 Regular os/exec
is high-level, therefore I went for syscall.ForkExec
and produced the following code: 常规的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)
}
I have also made a simple application that sleeps and prints hello world and put it to path. 我还制作了一个简单的应用程序,可以睡眠并打印hello world并将其放入路径。
#include <stdio.h>
int main(){
while(1){
printf("hello world");
fflush(stdout);
usleep(300000);
}
}
It works, however, the process is not send to background as I expected, my go process still owns the child. 但它的工作原理并没有像我预期的那样发送到后台,我的go进程仍然拥有这个孩子。 The SysProcAttr
has the following values in Linux: 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
}
I also tried the following but It caused an error: 我也尝试了以下但它导致了一个错误:
Sys: &syscall.SysProcAttr{
Setsid: true,
Setctty: true,
Foreground: false,
},
Spawned proc 0 inappropriate ioctl for device
Also the following: 还有以下内容:
Sys: &syscall.SysProcAttr{
Setsid: true,
Setctty: true,
Foreground: false,
Noctty: true,
Setpgid: true,
},
Spawned proc 0 operation not permitted (with root privilleges)
What am I doing/assuming wrong? 我在做什么/假设错了? Note: Despite saying os/exec is high-level, I also tried the following, but it produced same results. 注意:尽管说os / exec是高级的,我也尝试了以下,但它产生了相同的结果。
cs := exec.Command(binary)
cs.SysProcAttr = &syscall.SysProcAttr{
Setctty: true,
}
err := cs.Run()
fmt.Println(err)
A process forked with Start()
will continue even after its parent dies. 使用Start()
分叉的进程即使在其父级死亡后也将继续。
func forker() {
cmd := exec.Command("sleep", "3")
cmd.Start()
time.Sleep(2 * time.Second)
os.Exit(1)
}
Here the sleep
process will happily live on for 3 seconds even if the parent process only lives for 2 seconds: 即使父进程仅存在2秒, sleep
过程也会幸运地持续3秒:
$ 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
Notice how the parent process ID ( PPID
) of the sleep
process became 1
when the parent process 71432
exited. 注意当父进程71432
退出时, sleep
进程的父进程ID( PPID
)如何变为1
。 This means that the sleep
process has been orphaned. 这意味着sleep
过程已经成为孤儿。
The Start() method should give you what you're looking for. Start()方法应该为您提供所需的内容。 My script to append a simple text file after my sample Go program terminated continued to run: 我的脚本在我的示例Go程序终止后附加一个简单的文本文件继续运行:
package main
import (
"os/exec"
)
func main() {
cmd := exec.Command("./appender.sh")
cmd.Start()
}
It seems good strategy for immediately disowning is spawning os.Argv[0]
process (self-spawn), then spawning target process (with Setsid: true), and then exiting first spawned process. 立即os.Argv[0]
好策略是产生os.Argv[0]
进程(自生成),然后生成目标进程(使用Setsid:true),然后退出第一个生成的进程。 This way gradchild process immediately gets ppid 1. Optionally first spawned process can communicate pid of granchild via stdout to parent before it exits. 这样,gradchild进程立即获得ppid 1.可选的第一个衍生进程可以在退出之前通过stdout将granchild的pid传递给父进程。
Daemonization doesn't play well with go's goroutine scheduling, so the details get pretty gnarly. 守护进程不适合go的goroutine调度,所以细节变得非常粗糙。 godaemon has a small implementation with good discussion and links to more good discussion, and there's also the more popular and more traditionally implemented go-daemon . godaemon有一个很小的实现,有很好的讨论和更好的讨论链接,还有更流行,更传统的go-daemon 。
The ioctl
errors you are getting almost certainly come from the use of Setctty
. 几乎可以肯定, ioctl
错误来自于使用Setctty
。
Sys: &syscall.SysProcAttr{ Foreground: false, Setsid: true, },
should get you semantics like os/exec
's Start
, where the child process is not immediately reparented, but will safely be hoisted if/when the parent dies. 应该让你获得像os/exec
的Start
这样的语义,其中子进程不会立即被重新定义,但是如果/当父进程死亡时将安全地被提升。
The operation not permitted
you're seeing is, I think, about the pipes in /tmp/
, from running under 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)
我认为, operation not permitted
你看到的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)
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)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.