让我们说我在Go中实施kill
程序。我可以从命令行接受数字信号和PID,并将它们发送到syscall.Kill
没问题。
但是,我不知道如何实现"字符串"信号调度的形式,例如kill -INT 12345
。
真实用例是一个较大程序的一部分,它会提示用户发送终止信号;不能代替kill
。
问题:
如何在运行时将有效信号名称转换为任何支持平台上的信号编号(或者至少不编写在编译时运行的每个平台代码)?
我尝试过的事情:
kill -l
返回不同的信号列表,而不是现代Linux而不是旧Linux。使这个解决方案工作的唯一方法是为每个操作系统制作地图,这需要我了解每个操作系统的行为,并在添加新的信号支持时保持最新。kill
工具并从中捕获信号列表。这是不优雅的,有点悖论,还需要a)能够找到kill
,b)具有执行子进程的能力/权限,以及c)能够预测/解析{{1}的输出}} - 的二元kill
类型' Signal
方法。这只返回包含信号编号的字符串,例如String
,这没用。runtime.signame
,这正是我想要的。 os.Signal(4).String() == "signal 4"
黑客行为会有效,但我认为这种事情是不可理喻的。我避开的想法/事情
go://linkname
开头的syscall
成员。我被告知这是不可能的,因为名字被编译掉了;是否有可能,对于像信号名称这样基本的东西,他们在 的某个地方被编译掉了?答案 0 :(得分:0)
由于没有发布任何答案,我将发布一个不太理想的解决方案,我可以使用"闯入" Go标准库中的私有信号枚举功能。
signame
内部函数可以在Unix和Windows上按编号获取信号名称。要调用它,您必须使用linkname
/assembler workaround。基本上,在你的项目中创建一个名为empty.s
或类似的文件,没有内容,然后是一个像这样的函数声明:
//go:linkname signame runtime.signame
func signame(sig uint32) string
然后,您可以通过调用signame
增加的数字来获取操作系统已知的所有信号的列表,直到它没有返回值,如下所示:
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并不认为操作系统有一个信号名称(kill(1)可能会命名一些Go赢得的信号,我找不到它的名字,我发现了,但它通常是较高编号/非标准的),或字符串名称,例如" INT"哪里可以找到名字。
此行为未记录,可能会发生变化,并且在某些平台上可能不成立。如果将其公之于众,那将会很好。
答案 1 :(得分:0)
Commit d455e41在2019年3月以sys/unix.SignalNum()
的形式添加了此功能,因此至少从Go 1.13开始可用。更多详细信息,请参见GitHub第#28027。
来自golang.org/x/sys/unix
软件包的documentation:
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)
}
}