简体   繁体   English

Golang exec进程并取消它

[英]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/execStart这样的语义,其中子进程不会立即被重新定义,但是如果/当父进程死亡时将安全地被提升。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM