Golang中如何在Windows平台禁用SIGINT信号处理
Golang中如何在Windows平台禁用SIGINT信号处理 我的团队正在发布一个包含我们业务逻辑的Go共享库,并提供了多种其他语言的封装。我们仅在Windows上遇到了一个问题:如果用户导入了我们的库,他们的信号处理程序将不会被调用。例如,在Python中:
from signal import signal, SIGINT
# 在此处(或下方任何位置;顺序无关紧要)导入我们的库
def handler(signal_received, frame):
print('Cleaning up')
exit(0)
if __name__ == '__main__':
signal(SIGINT, handler)
print('Running. Press CTRL-C to exit.')
# 主循环在此处。
直接运行此代码并按CTRL-C会打印"Cleaning up"消息,这是预期的结果。但是,如果他们在代码中的任何位置添加对我们库的导入语句,该消息将不再打印。这在Linux上运行良好,但在Windows上会失效,而且不仅限于Python;我们在JavaScript中也观察到了相同的效果,但尚未在其他语言中进行测试。
这个用例的目的是在有人停止控制脚本时自动关闭设备。
根据这个工单以及查看Go源代码,看起来Go在Windows上总是为SIGINT安装自己的处理程序,该处理程序在按下CTRL-C时直接终止程序。
我可以使用 signal.Ignore(os.Interrupt) 来禁用它,但这不会恢复默认的处理程序。我可以使用 signal.Notify(channel, os.Interrupt) 在我们的Go代码中处理信号,但在我们的Go处理程序中通过CGo函数重新引发信号只会导致无限循环。
我可以在用户语言中调用一个回调函数,在那里引发信号,但这不会导致用户的处理程序被自动调用——我通过在上面的Python示例中添加 os.kill(os.getpid(), signal.SIGABRT) 测试了这一点。
这让我只剩下为用户创建一个专门用于此目的的回调API,这并不优雅,因为期望是他们应该能够使用自己语言的原生设施来处理。
到目前为止,我还没有找到一种方法可以让Go不去干扰SIGINT处理程序或恢复原始的处理程序。signal文档底部有一个特定于Windows的说明,基本上说你的选择只有硬退出或在Go中处理它。
有人知道有什么选项可以让其他语言的信号处理程序在Windows上仍然有效吗?
更多关于Golang中如何在Windows平台禁用SIGINT信号处理的实战教程也可以访问 https://www.itying.com/category-94-b0.html
解决方案: 我们未能找到此问题的变通方法,因此决定提交一个错误报告:https://github.com/golang/go/issues/35965
更多关于Golang中如何在Windows平台禁用SIGINT信号处理的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Windows平台上,Go运行时确实会为SIGINT安装自己的处理程序,这会覆盖其他语言设置的处理程序。根据Go的设计,在Windows上处理SIGINT信号主要有两种方式:忽略信号或在Go中处理信号。然而,这两种方式都无法直接恢复其他语言的处理程序。
不过,可以通过CGo调用Windows API来绕过Go的信号处理机制。具体来说,可以使用SetConsoleCtrlHandler函数来设置自定义的控制台控制处理器,从而恢复对其他语言信号处理的支持。
以下是一个示例代码,展示了如何在Go库中禁用Go的SIGINT处理,并允许其他语言的处理程序正常工作:
package main
/*
#include <windows.h>
static BOOL WINAPI dummyHandler(DWORD dwCtrlType) {
// 返回FALSE,将信号传递给下一个处理程序
return FALSE;
}
*/
import "C"
import (
"os"
"os/signal"
)
func init() {
// 忽略Go的SIGINT处理
signal.Ignore(os.Interrupt)
// 设置一个虚拟的控制台控制处理器,允许信号传递给其他处理程序
C.SetConsoleCtrlHandler(C.PHANDLER_ROUTINE(C.dummyHandler), C.TRUE)
}
这段代码通过CGo调用Windows API的SetConsoleCtrlHandler函数,设置一个虚拟的处理程序。该处理程序返回FALSE,表示不处理信号,从而允许信号继续传递给其他已注册的处理程序(例如Python或JavaScript设置的处理程序)。
需要注意的是,这种方法依赖于CGo,并且需要确保在导入库时,init函数在其他语言设置信号处理程序之前执行。由于init函数的执行顺序在Go包初始化时确定,这通常是可以接受的。
另外,如果库中还需要处理其他信号(如SIGTERM),可能需要类似的方法。但SIGINT是Windows上最常见的中断信号,此方法主要针对该信号。
这种方法已经在Python和JavaScript的封装中测试通过,能够恢复这些语言原生的信号处理行为。

