我有一个控制台模式的Windows应用程序(从Unix移植),最初设计为在接收到^C (Unix SIGINT
)时进行干净的退出。在这种情况下,干净的退出需要等待(可能相当长的时间)远程网络连接关闭。(我知道这不是^C的正常行为,但我不能改变它。)程序是单线程的。
我可以用signal(SIGINT)
(如在Unix下)或SetConsoleCtrlHandler
捕获^C。当程序在CMD.EXE下运行时,两者都可以正常工作。但是,如果我使用MSYS附带的"bash"shell(我使用MinGW环境来构建程序,因为这允许我重用Unix makefiles),那么程序将在^C之后随机短时间(小于100毫秒)强制终止。这是不可接受的,因为正如我提到的,程序需要等待远程网络连接关闭。
人们很可能想要在MSYS bash下运行这个程序。而且,这种效果会破坏测试套件。我没能找到任何方法来解决这个问题,无论是从程序内(理想)还是通过shell上的设置(可接受)。有人能推荐点什么吗?
我遇到了完全相同的问题——我写了一个带有SIGINT/SIGTERM处理程序的程序。这个处理程序做清理工作,有时需要一段时间。当我在msys bash中运行程序时,ctrl-c会导致SIGINT处理程序触发,但它不会结束-程序在完成清理工作之前被终止("从外部")。
基于phs的答案,以及对类似问题https://stackoverflow.com/a/23678996/2494650的答案,我提出了以下解决方案。它非常简单,而且可能有一些我还没有发现的副作用,但它为我解决了这个问题。
创建一个~/。
trap '' SIGINT
就是这样。这将捕获信号,并防止msys bash"从外部"终止您的程序。然而,它仍然以某种方式让SIGINT信号通过您的程序,允许它执行其优雅的清理/关闭。我不能确切地告诉你为什么它是这样工作的,但它确实是这样的——至少对我来说是这样的。
祝你好运!
这可能是由于臭名昭著的mintty"与外星程序的输入/输出交互"问题(又名mintty问题#56)。在这种情况下,它表现为Ctrl-C突然终止程序,而不是作为要捕获和处理的信号传递给程序。这个理论的证据是基于zwol的广泛解释:"控制台模式的Windows应用程序","[应用程序]被设计为在接收到^C时做一个干净的退出","[应用程序]在CMD.EXE下运行时正确工作",但是"[当使用终端时]自带MSYS[…](在撰写本文(2018)时,MSYS默认使用mintty作为其终端)。
不幸的是,mintty不是一个完整的Windows控制台替代品,并且"本地"Windows程序所期望的各种行为没有实现。但是,当您在mintty中运行这些本机程序时,您可能会喜欢将它们包装在winpty中…
其他问题也描述了这种行为:参见https://superuser.com/questions/606201/how-to-politely-kill-windows-process-from-cygwin和https://superuser.com/questions/1039098/how-to-make-mintty-close-gracefully-on-ctrl-c。
Arg - 5分钟编辑评论。下面是我想写的:
作为一种解决方法,而不是试图捕获CTRL-C事件,它也被传播到shell,我建议关闭stdin上的ENABLED_PROCESSED_INPUT,以便CTRL-C被报告为键盘输入,而不是作为信号:
DWORD mode;
HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE);
GetConsoleMode(hstdin, &mode);
SetConsoleMode(hstdin, mode & ~ENABLE_PROCESSED_INPUT); /* disable CTRL-C processing as a signal */
你可以在主线程中处理键盘输入,而程序的其余部分在一个单独的线程中做它的事情,并在收到CTRL-C时设置一个事件来清除。
当您使用MSYS bash运行程序时,您是直接运行可执行文件,还是有一个包装(bash) shell脚本?
如果是这样,它可能是用trap
命令注册一个自定义Ctrl-C处理程序(它执行睡眠,然后执行杀死)。如果这样的东西存在,修改或删除它。
如果没有注册trap
,或者没有包装脚本,请考虑制作这样的脚本并添加您自己的陷阱来覆盖默认行为。您可以在这里或bash的手册页(在SHELL BUILTINS一节中)看到如何使用它的示例。
Ctrl-C是SIGINT?我以为Ctrl-Z是SIGINT,但Ctrl-C是SIGTERM。检查。
您是否有CYGWIN环境设置(在控制面板/环境变量中)?尝试设置CYGWIN=notty并重新启动一个新的MSYS bash shell -问题仍然存在吗?