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: %v\n", sig)
// ✅ 此处可加入优雅退出逻辑,如关闭资源、等待任务完成等
if sig == os.Interrupt || sig == os.Kill {
fmt.Println("Shutting down gracefully...")
done <- true
return
}
}
}()
fmt.Println("Waiting for signals...")
<-done
fmt.Println("Exiting cleanly.")
}? 注意事项:
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 服务器、守护进程)的基础能力。