Golang中goroutines导致程序崩溃(收到signal killed信号)

Golang中goroutines导致程序崩溃(收到signal killed信号) 我有一个应用程序会启动多个goroutine,如下所示:

func ExecProcess(cmdname string, params *[]string)  {
cmd := exec.Command(cmdname, (*params)[0:]...)
if err := cmd.Start(); err != nil {
	println(err.Error())
	return
}

// Wait for the process to finish or kill it after a timeout (whichever happens first):
done := make(chan error, 1)
go func() {
	done <- cmd.Wait()
}()
select {
case <-time.After(45 * time.Second):
	if err := cmd.Process.Kill(); err != nil {
		println("failed to kill process: " + err.Error())
	}
	println("process killed as timeout reached")
case err := <-done:
	if err != nil {
		println("process finished with error = " + err.Error())
	}

}

}

在Linux系统下,我的应用程序被终止(信号终止)并显示以下消息: process finished with error = signal killed

你知道如何防止这种情况发生吗?

此致


更多关于Golang中goroutines导致程序崩溃(收到signal killed信号)的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

这只是一个内存不足的问题

更多关于Golang中goroutines导致程序崩溃(收到signal killed信号)的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好,@Fookiwara,你是说运行你的 ExecProcess 函数的 Go 程序被终止了,还是你从 ExecProcess 中运行的程序被终止了?如果是后者,那么在我看来它似乎是被超时终止的。

问题分析:您的程序收到"signal killed"通常是因为系统资源不足(如内存不足)导致OOM Killer终止进程,或者是goroutine泄漏导致资源耗尽。在您的代码中,主要问题可能是:

  1. 未正确处理子进程的退出
  2. 未限制并发goroutine数量
  3. 未回收子进程资源

解决方案:

func ExecProcess(cmdname string, params []string) error {
    cmd := exec.Command(cmdname, params...)
    
    // 设置进程组ID以便正确杀死整个进程树
    cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
    
    if err := cmd.Start(); err != nil {
        return err
    }

    done := make(chan error, 1)
    go func() {
        done <- cmd.Wait()
    }()

    select {
    case <-time.After(45 * time.Second):
        // 杀死整个进程组
        if err := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL); err != nil {
            return fmt.Errorf("failed to kill process: %v", err)
        }
        return fmt.Errorf("process killed as timeout reached")
    case err := <-done:
        return err
    }
}

// 使用worker pool限制并发goroutine数量
type ProcessPool struct {
    sem chan struct{}
}

func NewProcessPool(maxWorkers int) *ProcessPool {
    return &ProcessPool{
        sem: make(chan struct{}, maxWorkers),
    }
}

func (p *ProcessPool) ExecProcess(cmdname string, params []string) error {
    p.sem <- struct{}{}
    defer func() { <-p.sem }()
    
    return ExecProcess(cmdname, params)
}

// 使用示例
func main() {
    pool := NewProcessPool(10) // 限制最多10个并发进程
    
    var wg sync.WaitGroup
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            err := pool.ExecProcess("sleep", []string{"10"})
            if err != nil {
                fmt.Printf("Process %d failed: %v\n", i, err)
            }
        }(i)
    }
    wg.Wait()
}

关键改进点:

  1. 使用Setpgid创建进程组,确保能正确杀死整个进程树
  2. 使用worker pool模式限制并发goroutine数量
  3. 使用sync.WaitGroup确保所有goroutine正确完成
  4. 改进错误处理,返回错误而不是直接打印
  5. 使用SIGKILL确保进程被强制终止

这样可以避免资源泄漏和过度并发导致的系统资源耗尽问题。

回到顶部