Linux Golang exec流程和否认它
我试图用我的守护进程来分叉进程,并试图在守护进程崩溃的情况下与它们断开连接。常规的Linux Golang exec流程和否认它,linux,go,fork,exec,Linux,Go,Fork,Exec,我试图用我的守护进程来分叉进程,并试图在守护进程崩溃的情况下与它们断开连接。常规的os/exec是高级的,因此我选择了syscall.ForkExec并生成了以下代码: package main import ( "fmt" "os" "os/exec" "syscall" "time" ) func main() { cmd := "myproc" binary, lookErr := exec.LookPath(cmd)
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,并将其置于path
#include <stdio.h>
int main(){
while(1){
printf("hello world");
fflush(stdout);
usleep(300000);
}
}
我还尝试了以下操作,但出现了错误:
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)
你应该得到你想要的东西。示例Go程序终止后附加简单文本文件的脚本继续运行:
package main
import (
"os/exec"
)
func main() {
cmd := exec.Command("./appender.sh")
cmd.Start()
}
与
Start()
分叉的进程即使在其父进程死亡后也将继续
func forker() {
cmd := exec.Command("sleep", "3")
cmd.Start()
time.Sleep(2 * time.Second)
os.Exit(1)
}
在这里,睡眠
进程将愉快地持续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
请注意当父进程退出时,
sleep
进程的父进程ID(PPID
)如何变成1
。这意味着sleep
进程已被孤立。立即断开连接的好策略是生成os.Argv[0]
进程(自生成),然后生成目标进程(setId:true),然后退出第一个生成的进程。这样,gradchild进程立即获得PPID1。可选地,第一个生成的进程可以在退出之前通过标准输出将granchild的pid传递给父进程。守护进程化与go的goroutine调度不匹配,因此细节变得非常复杂。有一个小的实现,有好的讨论和链接到更多好的讨论,还有更流行和更传统的实现
几乎可以肯定,您所遇到的ioctl
错误来自于使用
Sys:&syscall.SysProcAttr{
前景:错,
塞西德:是的,
},
应该得到类似于os/exec
的Start
的语义,其中子进程不会立即重新租用,但在父进程死亡时会安全地被提升
func forker() {
cmd := exec.Command("sleep", "3")
cmd.Start()
time.Sleep(2 * time.Second)
os.Exit(1)
}
您看到的操作是不允许的
,我想是关于/tmp/
中的管道在strace下运行:
openat(AT_FDCWD,“/tmp/stdin”,O_RDWR | O|u CREAT | O|u TRUNC | O|u CLOEXEC,0666)=3
epoll_create1(epoll_CLOEXEC)=4
epoll_ctl(4,epoll_ctl_ADD,3,{EPOLLIN|EPOLLOUT | epolldhup | EPOLLET,{u32=471047936,u64=140179613654784}})=-1 EPERM(不允许操作)
epoll_ctl(4,epoll_ctl_DEL,3,0xc420039c24)=-1 EPERM(不允许操作)
openat(AT_FDCWD,“/tmp/stdout”,O_RDWR | O|u CREAT | O|u TRUNC | O|u CLOEXEC,0666)=5
epoll_ctl(4,epoll_ctl_ADD,5,{EPOLLIN|EPOLLOUT | epolldhup | EPOLLET,{u32=471047936,u64=140179613654784}})=-1 EPERM(不允许操作)
epoll_ctl(4,epoll_ctl_DEL,5,0xc420039c24)=-1 EPERM(不允许操作)
openat(AT_FDCWD,“/tmp/stderr”,O_RDWR | O|u CREAT | O|u TRUNC | O|u CLOEXEC,0666)=6
epoll_ctl(4,epoll_ctl_ADD,6,{EPOLLIN|EPOLLOUT | epolldhup | EPOLLET,{u32=471047936,u64=140179613654784}})=-1 EPERM(不允许操作)
epoll_ctl(4,epoll_ctl_DEL,6,0xc420039c24)=-1 EPERM(不允许操作)
为什么您认为exec.Cmd
的级别太高?您也可以从那里访问sysprocatr。另外,您的流程仍然拥有孩子是什么意思?是否在同一流程组中?如果父进程退出,它不会被重新分配到PID 1吗?请注意,如果命令使用来自父进程的stdin/stdout,如果父进程终止,进程可能会变成僵尸。@Anferne我正在执行一个Go程序的bash脚本,该程序在一个阶段内终止父进程Go。我在cmd.Start()之前提供了“stderr,\:=cmd.StderrPipe()”,并注意到运行脚本的子进程也会在父进程go进程被终止后死亡。一旦我删除了stderr行并执行了该程序,子进程就会正确地继续执行。如果我不明白你的意思,请原谅,但这就是你在上述评论中提到的吗?在上面的例子中,目标是使71432等于1(立即)。问题的措辞是:“[…]以防我的守护进程崩溃[sic]”。没有必要立即取消该过程。使用Start()。