如何在 Go 中以跨平台的方式按名称列出可用的操作系统信号?

How can I list available operating system signals by name in a cross-platform way in Go?

假设我正在用 Go 实现 kill 程序。我可以从命令行接受数字信号和 PID,并将它们发送到 syscall.Kill 没问题。

但是,我不知道如何实现 "string" 形式的信号调度,例如kill -INT 12345.

真正的用例是提示用户发送终止信号的较大程序的一部分;不能替代 kill.

问题:

如何在 运行 时(或至少不在编译时将每个平台的代码编写为 运行)在任何支持的平台上将有效信号名称转换为信号编号?

我试过的:

Ideas/Things我没试过:

由于 post 没有答案,我将 post 我能够通过 "breaking into" Go 中的私有信号枚举函数使用的不太理想的解决方案标准库。

signame internal function can get a signal name by number on Unix and Windows. To call it, you have to use the linkname/assembler workaround。基本上,在你的项目中创建一个名为 empty.s 或类似的文件,没有内容,然后是一个像这样的函数声明:

//go:linkname signame runtime.signame
func signame(sig uint32) string

然后,您可以通过对递增的数字调用 signame 直到它没有 return 一个值来获取操作系统已知的所有信号的列表,如下所示:

signum := uint32(0)
signalmap = make(map[uint32]string)
for len(signame(signum)) > 0 {
    words := strings.Fields(signame(signum))
    if words[0] == "signal"  || ! strings.HasPrefix(words[0], "SIG") {
        signalmap[signum] = ""
    } else {
        // Remove leading SIG and trailing colon.
        signalmap[signum] = strings.TrimRight(words[0][3:], ":")
    }
    signum++
}

运行后,signalmap 将拥有当前操作系统上可以发送的每个信号的键。它将有一个空字符串,其中 Go 认为 OS 没有信号名称(kill(1) 可能会命名一些 Go 不会 return 命名的信号,我'我已经找到了,但它通常是 higher-numbered/nonstandard 个)或字符串名称,例如"INT" 哪里可以找到名字。

此行为未记录在案,可能会发生变化,并且可能不适用于某些平台。不过,如果能做到这一点就好了public。

提交 d455e41 added this feature in March 2019 as sys/unix.SignalNum() and is thus available at least since Go 1.13. More details in GitHub issue #28027

来自 golang.org/x/sys/unix 包的 documentation

func SignalNum(s string) syscall.Signal

SignalNum returns the syscall.Signal for signal named s, or 0 if a signal with such name is not found. The signal name should start with "SIG".

要回答类似的问题,"how can I list the names of all available signals (on a given Unix-like platform)",我们可以使用反函数sys/unix.SignalName():

    import "golang.org/x/sys/unix"

    // See https://github.com/golang/go/issues/28027#issuecomment-427377759
    // for why looping in range 0,255 is enough.
    for i := syscall.Signal(0); i < syscall.Signal(255); i++ {
        name := unix.SignalName(i)
        // Signal numbers are not guaranteed to be contiguous.
        if name != "" {
            fmt.Println(name)
        }
    }