英文:
Start a process in Go and detach from it
问题
我需要在Go中启动一个新的进程,具有以下要求:
- 即使Go进程终止,启动的进程也应该继续运行
- 我需要能够设置运行该进程的Unix用户/组
- 我需要能够设置继承的环境变量
- 我需要对标准输入/输出/错误进行控制
以下是一个尝试的代码示例:
var attr = os.ProcAttr {
Dir: "/bin",
Env: os.Environ(),
Files: []*os.File{
os.Stdin,
os.Stdout,
os.Stderr,
},
}
process, err := os.StartProcess("sleep", []string{"1"}, &attr)
这段代码可以正常工作,但与要求相比有以下缺点:
- 没有设置Unix用户/组的方法
- 当Go进程(父进程)停止时,启动的进程也会结束
如果只需要在Linux上运行,可能会简化问题。
英文:
I need to start a new process in Go with the following requirements:
- The starting process should run even after the Go process is terminated
- I need to be able to set the Unix user/group that's running it
- I need to be able to set the environment variables inherited
- I need control over std in/out/err
Here is an attempt:
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)
This works fine but has the following shortcomings from the requirements:
- No way to set Unix user/group
- The started process ends when the Go process (parent) stops
This needs to run on Linux only if that simplifies things.
答案1
得分: 24
你可以使用process.Release
来将子进程与父进程分离,并使其在父进程死亡后继续运行。
*os.ProcAttr.Sys.Credentials
属性的定义如下:通过使用该属性,你可以设置进程的用户和组ID。
以下是你示例的工作版本(我没有检查进程ID是否实际上是设置的那个):
package main
import "fmt"
import "os"
import "syscall"
const (
UID = 501
GUID = 100
)
func main() {
// Credential字段用于设置进程的UID、GID和附加的GIDS
// 你需要以root身份运行程序才能这样做
var cred = &syscall.Credential{UID, GUID, []uint32{}}
// Noctty标志用于将进程与父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 {
// 文档中并不清楚,但是Release实际上是将进程分离
err = process.Release()
if err != nil {
fmt.Println(err.Error())
}
} else {
fmt.Println(err.Error())
}
}
希望对你有帮助!
英文:
- You can use process.Release to detach the child process from the parent one and make it survive after parent death
- Look at the definition of *os.ProcAttr.Sys.Credentials attribute : it looks like using the attribute you can set process user and group ID.
Here is a working version of your example (I did not check if process ID's where actually the one set )
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())
}
}
答案2
得分: 2
我发现似乎可以跨平台地重新运行程序,只需使用一个特殊的标志重新运行程序。在你的主程序中,检查是否存在这个标志。如果在启动时存在,那么你就处于“分叉”状态。如果不存在,重新运行带有该标志的命令。
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
。当你的程序启动时,检查是否存在 --detached
标志,以确定是否需要调用 rerunDetached
。这有点像一个适用于不同操作系统的简易版 fork()
。
英文:
What I have found that seems to work cross-platform is to re-run the program with a special flag. In your main program, check for this flag. If present on startup, you're in the "fork". If not present, re-run the command with the flag.
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
}
This will simply re-run your process with the exact parameters and append --detached
to the arguments. When your program starts, check for the --detached
flag to know if you need to call rerunDetached
or not. This is sort of like a poor mans fork()
which will work across different OS.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论