
本文介绍使用 golang.org/x/sys/unix 包提供的 SignalNum 和 SignalName 函数,以纯 Go、无需 CGO 的方式实现信号名称与编号之间的双向转换,完全兼容 Linux、macOS 等主流 Unix-like 系统。
本文介绍使用 `golang.org/x/sys/unix` 包提供的 `signalnum` 和 `signalname` 函数,以纯 go、无需 cgo 的方式实现信号名称与编号之间的双向转换,完全兼容 linux、macos 等主流 unix-like 系统。
在 Go 中实现类似 kill -INT 12345 这样的字符串形式信号发送功能,关键在于将用户输入的信号名(如 "INT"、"TERM"、"KILL")安全、准确地解析为对应的操作系统信号编号(如 syscall.SIGINT)。早期 Go 标准库并未暴露信号名到编号的映射能力,开发者常被迫维护静态字典或调用外部命令,既不健壮也不跨平台。
幸运的是,自 Go 1.13 起(源于 golang.org/x/sys/unix v0.0.0-20190308072605-d455e41777fc),官方扩展包提供了两个核心函数:
-
unix.SignalNum(name string) syscall.Signal:根据信号名称(如"SIGINT"或"INT")返回对应的syscall.Signal值;若未找到则返回0; -
unix.SignalName(sig syscall.Signal) string:根据信号值反向查询其标准名称(如syscall.SIGTERM→"TERM")。
这两个函数均基于运行时实际加载的系统头文件(通过 cgo 在 x/sys/unix 内部生成,但对使用者完全透明),因此天然支持各平台差异——无需手动维护 macOS/Linux/BSD 的信号列表,也无需条件编译或运行时探测。
✅ 正确用法示例
package main
import (
"fmt"
"syscall"
"golang.org/x/sys/unix"
)
func main() {
// 名称 → 编号:支持 "SIGxxx" 和 "xxx" 两种格式
if sig := unix.SignalNum("INT"); sig != 0 {
fmt.Printf("INT → %d\n", sig) // 输出:INT → 2
}
if sig := unix.SignalNum("SIGQUIT"); sig != 0 {
fmt.Printf("SIGQUIT → %d\n", sig) // 输出:SIGQUIT → 3
}
// 编号 → 名称:用于展示或日志
fmt.Printf("SignalName(15): %s\n", unix.SignalName(syscall.SIGTERM)) // TERM
// 列出所有可用信号名称(推荐范围:1–128,避免平台越界)
fmt.Println("Available signals:")
for i := syscall.Signal(1); i <= syscall.Signal(128); i++ {
if name := unix.SignalName(i); name != "" {
fmt.Printf(" %s (%d)\n", name, i)
}
}
}⚠️ 注意事项:
SignalNum接受"INT"或"SIGINT"均可,内部自动标准化处理;- 信号编号非严格连续(例如某些平台
SIGRTMIN后存在大量空洞),因此遍历时建议使用1..128或1..255范围并跳过空名称,而非硬编码全部 256 个值;- 该功能仅适用于 Unix-like 系统(Linux/macOS/FreeBSD 等);Windows 不适用(其信号模型不同,Go 中
syscall.Kill在 Windows 上仅支持os.Interrupt和os.Kill的模拟语义);- 需要显式导入
golang.org/x/sys/unix并确保模块已更新(go get golang.org/x/sys/unix@latest)。
✅ 总结
借助 golang.org/x/sys/unix.SignalNum 和 SignalName,你可以在不引入 CGO、不依赖 shell 工具、不维护多平台字典的前提下,实现真正跨平台、运行时可靠的信号名称解析。这对构建交互式进程管理工具、调试辅助命令或增强 CLI 可用性至关重要——简洁、安全、且由 Go 官方生态原生支持。


















