Golang Go语言中怎么保证进程结束时能关闭掉子进程(多次监听信号量的问题)

发布于 1周前 作者 ionicwang 来自 Go语言

场景:

程序依赖另外一个程序,并开放个端口。遂启动时先 process:=exec.Command() process.Start().

问题来了,每次 ctrl+c 退出时,这个子进程就孤儿了。

所以想:

监听个 SIGINT 信号,信号来时直接 p.Kill()。测试,好使。

然后就封装了一下。

#伪代码
#proj/intenal/process/process.go
func(p *process) Go() {
    p.cmd.Start()
    chan ...signal
    os.Notify....
    go func  <-chan; p.cmd.Kill()
}

新问题:ctrl+c 后。子进程退出了,主进程还活着。

不想 在项目里显式的 exec.Command(....一堆。

既然连 exec.Command 都没有了。也就不想在最外层监听信号再传到自己封的 process 里。

那么问题来了:怎么能在内部包的里面使用信号还不影响信号本身对程序的影响呢?

其实就是想:

#main
func main() {
    internal.process.New().Go() # 一行,没有信号,没有需要关注的 ctx
    http.ServerStart()
    https.ServerStart()
    rpc.ServerStart()
# 整个 main 就几行就行了。

}


Golang Go语言中怎么保证进程结束时能关闭掉子进程(多次监听信号量的问题)

更多关于Golang Go语言中怎么保证进程结束时能关闭掉子进程(多次监听信号量的问题)的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

10 回复

server 为什么要 ctrl+c 终止?

更多关于Golang Go语言中怎么保证进程结束时能关闭掉子进程(多次监听信号量的问题)的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


可以试试
flag := unix.SIGHUP
if err := unix.Prctl(unix.PR_SET_PDEATHSIG, uintptr(flag), 0, 0, 0); err != nil {
return
}

看了文档,当你监听了 SIGINT 信号,就相当于覆盖了 go 对 SIGINT 的默认行为(退出程序)

简单方法:让 main 函数也监听 SIGINT 信号。

go1.19 的 SysProcAttr 新增一个 Pdeathsig

在 start 前 cmd.Process.SysProcAttr=&syscall.SysProcAttr{ Pdeathsig: syscall.SIGTERM} 即可。

不知道有啥坑。https://go.dev/issue/27505.

本地开发贼鸡儿麻烦。

exec.CommandContext() 方法,你 ctrl-c 的时候 cancel parent context ,起的这个子进程会被自动 kill 掉。

豆瓣曾经开源过一个名字很没品的项目:曹娥

https://github.com/douban/CaoE

为啥不使用 socketpair ?进程退出,另一端套接字直接 read 返回 0

按你的需求,如果我没理解错的话,直接在监听 SIGINT 的自定义方法末尾补个 os.Exit 就好了吧

在Golang中,确保进程结束时能够正确关闭子进程,通常涉及对操作系统信号量的监听和处理。这可以通过os/signal包和syscall包来实现。对于多次监听信号量的问题,可以通过设置一个全局的信号通道来避免重复监听。

以下是一个示例代码,展示了如何在Golang中监听系统信号并关闭子进程:

package main

import (
	"fmt"
	"os"
	"os/exec"
	"os/signal"
	"syscall"
	"time"
)

func main() {
	// 启动子进程
	cmd := exec.Command("your_command_here")
	cmd.Start()
	defer cmd.Process.Kill() // 确保在main函数退出时尝试杀死子进程

	// 设置信号通道,只监听一次
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

	// 等待信号
	sig := <-sigs
	fmt.Printf("Received signal: %s\n", sig)

	// 给子进程一些时间来清理
	time.Sleep(2 * time.Second)

	// 强制杀死子进程(如果还未退出)
	if err := cmd.Process.Kill(); err != nil {
		fmt.Printf("Failed to kill subprocess: %v\n", err)
	}

	fmt.Println("Main process exiting.")
}

在这个示例中,我们使用signal.Notify来监听系统信号,并在接收到信号后给子进程一些时间来执行清理操作。最后,如果子进程还未退出,我们强制杀死它。注意,这种方法依赖于defer和显式的Kill调用来确保子进程在程序退出时能够被正确关闭。

回到顶部