tl; dr,如果信号可以在go运行时中随时处理,那么我们如何安全地使用signal.Ignore
SIGINT来避免在安装默认信号处理程序和main()
运行我们的内部指令之间产生竞争
在旅途中的文档进行包装/信号指出此有关的信号的默认行为
SIGHUP,SIGINT或SIGTERM信号导致程序退出。
因此,编写一个旋转CPU的golang二进制文件,按ctrl + c发送SIGINT,程序将退出。
现在,假设您要覆盖该行为。一种方法是忽略带有signal.Ignore(syscall.SIGINT)
但是现在考虑以下
package main
import (
"fmt"
"os/signal"
"syscall"
"time"
)
func main() {
fmt.Println("Looping, SIGINT will have default behavior")
for start := time.Now(); time.Now().Before(start.Add(time.Second * 5)); {
}
signal.Ignore(syscall.SIGINT)
fmt.Println("OK")
for start := time.Now(); time.Now().Before(start.Add(time.Second * 5)); {
}
}
在这里,我们有一个简单的golang二进制文件,它会循环5秒钟。这是一个繁忙的循环,因此我们知道我们不会通过在OS线程上浪费时间来参与协作式多任务处理。然后,它注册以忽略SIGINT。
如果尝试这样做,您会注意到,如果在前5秒钟内输入ctrl + c,程序将退出。这似乎是有道理的-我们正在获取默认的golang运行时信号处理行为,因为我们尚未通过调用来覆盖它signal.Ignore
。
现在也许我们可以通过将移动signal.Ignore
到main()中的第一位来解决此问题,但是此程序证明的是go运行时不能保证默认信号处理程序不会在main()中的同步代码之前运行完成执行。
即使您移动它,我们似乎也在
我找不到关于此的文档。Go运行时提供什么保证,使我完全确定信号不会在第1阶段和第2阶段之间到达?
TL; DR:尽早抓住,例如,在的顶部main
。
正如评论所言,这似乎不是将其描述为问题的好方法,因为它在所有编程语言中都是通用的:如果您没有为设置信号处理程序syscall.SIGINT
,则默认情况下会被杀死SIGINT
,一旦拥有就不会。没错,但是任何程序在启动之前都可以杀死,然后才有机会开始捕获信号。不论编程语言如何,都是如此。它对C和C ++程序的影响与对Go程序的影响一样多。
因此,通常来说,您需要做的所有工作才能可靠地捕获或舍弃SIGINT
信号,并尽可能避免种族歧视,那就是尽早使用信号,即在通道signal.Notify(ch, os.Interrupt)
顶部附近。1然后,您可以编写自己的非竞赛代码,以通过goroutines和渠道进行处理:main
ch
os.Exit
(或-可能是更好的使用os.Reset
上syscall.SIGINT
,然后交付自己syscall.SIGINT
产生正确的操作系统级别的退出状态,“通过信号1杀害” 2)。如果信号应被忽略,只需放弃频道通知。有点相关:最近有一个修复程序可以syscall.SIGPIPE
处理。特别是,调用signal.Ignore(syscall.SIGPIPE)
应忽略SIGPIPE
,但不是。这似乎在Go 1.14中已修复。
1如软件包说明所述,故意捕获信号将绕过外壳中nohup
或trap "" 1 2 15
外壳中的任何“被忽略”状态。如果您想检查一下,请使用此signal.Ignored
功能。
2如果signal_unix.go
导出了该dieFromSignal
函数,则可以直接使用该函数,但不能。拥有一个与操作系统无关的包装器,至少可以干净地尝试这种自杀是很好的。这甚至可以sigprocmask
在操作系统级别使用,以使自杀尽可能地避免种族歧视。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句