
go 程序默认无法持续响应 `kill` 命令发送的信号(如 sigterm、sigint),根本原因是信号接收协程在处理一次信号后即退出;需通过循环阻塞读取通道,才能持续捕获后续信号。
在 go 中使用 os/signal 包监听系统信号时,一个常见误区是:仅执行一次 igs 操作,导致协程在接收首个信号(例如 CTRL+C 触发的 os.Interrupt)后立即返回,后续通过 kill -15
✅ 正确做法是:在 goroutine 中使用 无限 for 循环 + 阻塞接收,确保信号通道持续被消费:
package main import ( "fmt" "os" "os/signal" ) func main() { sigs := make(chan os.Signal, 1) done := make(chan bool, 1) // 注册所有默认可捕获信号(等价于 signal.Notify(sigs, os.Interrupt, os.Kill, syscall.SIGTERM, ...)) signal.Notify(sigs) go func() { for { // 关键:循环持续接收,避免协程退出 sig := <-sigs fmt.printf("received signal: %vn", sig)>? 注意事项:
- signal.Notify(sigs) 不传信号类型时,默认监听 os.Interrupt(Ctrl+C)和 os.Kill(不可捕获,仅用于 os.Exit),但不包含 SIGTERM(kill -15)或 SIGHUP 等常用信号。生产环境应显式指定:
signal.Notify(sigs, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
✅ 测试验证:编译运行后,在另一终端执行:
$ ./signal & $ kill -15 $! # → 输出 "Received signal: terminated" $ kill -USR1 $! # → 输出 "Received signal: user defined signal 1"
掌握这一模式,是构建高可靠性 Go 后台服务(如 Web 服务器、守护进程)的基础能力。