在Go中启动一个进程并将其从中分离

我需要在Go中启动一个新的stream程,并具有以下要求:

  • 即使在Go进程终止之后,启动进程也应该运行
  • 我需要能够设置运行它的Unix用户/组
  • 我需要能够设置inheritance的环境variables
  • 我需要控制std in / out / err

这是一个尝试:

var attr = os.ProcAttr { Dir: "/bin", Env: os.Environ(), Files: []*os.File{ os.Stdin, "stdout.log", "stderr.log", }, } process, err := os.StartProcess("sleep", []string{"1"}, &attr) 

这工作正常,但从要求有以下缺点:

  • 没有办法设置Unix用户/组
  • 当进程(父)停止时,启动的进程结束

只有在简化事情的情况下才需要在Linux上运行。

  1. 你可以使用process.Release从父进程中分离子进程,并在父进程结束后继续运行
  2. 看看* os.ProcAttr.Sys.Credentials属性的定义:它看起来像使用属性,你可以设置处理用户和组ID。

这里是你的例子的工作版本(我没有检查过程ID是实际上在哪里设置的)

 package main import "fmt" import "os" import "syscall" const ( UID = 501 GUID = 100 ) func main() { // The Credential fields are used to set UID, GID and attitional GIDS of the process // You need to run the program as root to do this var cred = &syscall.Credential{ UID, GUID, []uint32{} } // the Noctty flag is used to detach the process from parent tty var sysproc = &syscall.SysProcAttr{ Credential:cred, Noctty:true } var attr = os.ProcAttr{ Dir: ".", Env: os.Environ(), Files: []*os.File{ os.Stdin, nil, nil, }, Sys:sysproc, } process, err := os.StartProcess("/bin/sleep", []string{"/bin/sleep", "100"}, &attr) if err == nil { // It is not clear from docs, but Realease actually detaches the process err = process.Release(); if err != nil { fmt.Println(err.Error()) } } else { fmt.Println(err.Error()) } } 

我发现,似乎跨平台的工作是用一个特殊的标志重新运行程序。 在你的主程序中,检查这个标志。 如果在启动时出现,则处于“分叉”状态。 如果不存在,用标志重新运行命令。

 func rerunDetached() error { cwd, err := os.Getwd() if err != nil { return err } args := append(os.Args, "--detached") cmd := exec.Command(args[0], args[1:]...) cmd.Dir = cwd err = cmd.Start() if err != nil { return err } cmd.Process.Release() return nil } 

这将简单地用确切的参数重新运行你的进程,并追加 – 附加到参数。 当你的程序启动时,检查--detached标志来知道你是否需要调用rerunDetached 。 这就像一个可怜的男人fork() ,将跨不同的操作系统。