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

Zac*_*c B 5 unix signals cross-platform go

假设我正在kill用 Go 实现该程序。我可以接受来自命令行的数字信号和 PID,并将它们发送出去,syscall.Kill没有任何问题。

但是,我不知道如何实现信号调度的“字符串”形式,例如kill -INT 12345

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

问题:

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

我尝试过的:

  • 保留信号名称到数字的静态映射。kill -l这不能以跨平台的方式工作(例如, Mac OSX 与现代 Linux 与旧版 Linux 上返回不同的信号列表)。使该解决方案普遍有效的唯一方法是为每个操作系统制作地图,这需要我了解每个操作系统的行为,并在它们添加新信号支持时保持最新状态。
  • shell 到 GNUkill工具并从中捕获信号列表。这是不优雅且有点悖论的,并且还需要 a) 能够找到kill,b) 具有执行子进程的能力/权限,以及 c) 能够预测/解析kill二进制文件的输出。
  • 使用各种类型SignalString方法。这仅返回包含信号编号的字符串,例如os.Signal(4).String() == "signal 4",这没有用。
  • 调用私有函数runtime.signame,它正是我想要的。go://linkname黑客会起作用,但我认为这种事情不受欢迎是有原因的。

我没有尝试过的想法/事情:

  • 以某种方式使用 CGo。我宁愿不冒险进入 CGO 领域,因为这个项目根本不是低级/不需要本机集成的。如果这是唯一的选择,我会,但不知道从哪里开始。
  • 使用模板和代码生成在编译时根据外部源构建信号列表。出于与 CGo 相同的原因,这不是优选的。
  • syscall以某种方式反思和解析该成员SIG。有人告诉我这是不可能的,因为名字是被编译掉的;有没有可能,对于像信号名称这样基本的东西,在某些地方它们没有被编译掉?

mar*_*o.m 3

Commit d455e41于 2019 年 3 月添加了此功能,sys/unix.SignalNum()因此至少从 Go 1.13 起可用。更多详细信息,请参阅 GitHub 问题#28027

从包的文档golang.org/x/sys/unix中:

func SignalNum(s string) syscall.Signal

SignalNum 返回名为 s 的信号的 syscall.Signal,如果未找到具有此类名称的信号,则返回 0。信号名称应以“SIG”开头。

为了回答类似的问题,“如何列出所有可用信号的名称(在给定的类 Unix 平台上)”,我们可以使用反函数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)
        }
    }
Run Code Online (Sandbox Code Playgroud)