Golang中如何避免误杀由Go启动的Linux进程

Golang中如何避免误杀由Go启动的Linux进程 我需要从Go管理的HTML站点控制数据库和其他消息服务器。我可以启动这些进程,但它们会随着Go管理的HTML服务器一起结束。有没有办法让Go启动的进程在启动它们的Go例程结束(或崩溃)后继续存活?

6 回复

谢谢,它起作用了!Process.Release() 是一个隐藏得很深的函数,还是我在做梦?我认为这正是我需要的,再次感谢。

更多关于Golang中如何避免误杀由Go启动的Linux进程的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你需要释放子进程。

    cmd := exec.Command(binary, args...)
cmd.Dir = cwd

err = cmd.Start()
if err != nil {
	return err
}
err = cmd.Process.Release()

你所说的“go启动的进程”是什么意思?你指的是goroutine吗?就像go的html网站(服务器)启动一个goroutine来控制数据库,再启动另一个goroutine来处理消息传递?我不确定这是否可行,因为据我所知,当创建它们的主goroutine结束时,所有goroutine都会结束。

但如果你指的是启动的外部程序,我认为有exec.Start()可以用于此目的。

你可以做的是将这些goroutine转变为独立的程序/服务,并让它们能够从你的(html)Web服务器接收消息/指令。

是的,这正是我的意思。更具体地说,我有一个模拟物联网设备数据传输的 Go 程序。我希望能够从一个 HTML 页面(即正在运行的 Go 服务器)来控制这个程序。使用 exec.Start() 效果很好,但如果 HTML 服务器停止,我的物联网模拟器也会停止,这并不理想。我需要的是一个能创建新进程然后让其完全独立运行的过程。

我尝试过使用 StartProcess,但似乎由 Go 程序创建的每个进程实际上都是主程序的子进程。

func main() {
    fmt.Println("hello world")
}

更正:我无法让 cmd.Process.Release 正常工作。

有效的方法是:在 Go 代码中,使用 nohup& 启动子进程。从终端启动我的服务器,而不是从 Goland IDE。似乎 IDE 会让 Go 程序在一个父进程 ___go_build_main_go 下运行,该进程会强制终止所有子进程。如果我没理解错的话,nohup& 的作用是将进程与终端应用程序分离。我将在没有运行 Xorg 的 Linux 系统上测试它。通常,当一个进程变成孤儿进程时,它不会被杀死,而是会以一个相近的 PID 作为父进程(在正常情况下是 init),所以这个问题本不应该被提出。slight_smile

在Go中启动独立于父进程的Linux进程,可以使用syscall.SysProcAttr设置进程属性。关键是将进程设置为新的进程组领导,并确保父进程退出时子进程不被终止。

以下是实现方式:

package main

import (
    "log"
    "os"
    "os/exec"
    "syscall"
)

func startDetachedProcess(command string, args ...string) (*os.Process, error) {
    cmd := exec.Command(command, args...)
    
    // 设置进程属性
    cmd.SysProcAttr = &syscall.SysProcAttr{
        // 创建新的进程组
        Setpgid: true,
        // 可选的:设置子进程在父进程退出后不会被终止
        Pgid: 0,
    }
    
    // 重定向标准输入输出(可选)
    cmd.Stdin = nil
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    
    // 启动进程
    if err := cmd.Start(); err != nil {
        return nil, err
    }
    
    // 立即释放资源,不等待进程结束
    go func() {
        cmd.Wait()
    }()
    
    return cmd.Process, nil
}

func main() {
    // 示例:启动一个独立的数据库进程
    proc, err := startDetachedProcess("redis-server", "--port", "6379")
    if err != nil {
        log.Fatal("启动进程失败:", err)
    }
    
    log.Printf("已启动独立进程,PID: %d\n", proc.Pid)
    
    // Go程序可以安全退出,子进程会继续运行
    // 即使Go程序崩溃,子进程也不会被终止
}

更彻底的分离可以使用双重fork技术:

func startFullyDetached(command string, args ...string) error {
    attr := &syscall.SysProcAttr{
        Setsid: true,  // 创建新的会话
    }
    
    // 第一次fork
    pid, _, errno := syscall.RawSyscall(syscall.SYS_FORK, 0, 0, 0)
    if errno != 0 {
        return errno
    }
    
    if pid > 0 {
        // 父进程直接退出
        os.Exit(0)
    }
    
    // 子进程继续执行
    syscall.Setsid()
    
    // 第二次fork
    pid2, _, errno := syscall.RawSyscall(syscall.SYS_FORK, 0, 0, 0)
    if errno != 0 {
        os.Exit(1)
    }
    
    if pid2 > 0 {
        os.Exit(0)
    }
    
    // 最终的子进程执行目标命令
    syscall.Exec(command, args, os.Environ())
    return nil
}

对于需要管理多个后台进程的场景,可以考虑使用systemd或supervisor等进程管理工具,Go程序只需通过systemd API或配置文件来管理这些服务。

回到顶部