防止 MSYS 'bash' 杀死捕获 ^C 的进程

Posted

技术标签:

【中文标题】防止 MSYS \'bash\' 杀死捕获 ^C 的进程【英文标题】:Preventing MSYS 'bash' from killing processes that trap ^C防止 MSYS 'bash' 杀死捕获 ^C 的进程 【发布时间】:2011-10-28 11:13:37 【问题描述】:

我有一个控制台模式 Windows 应用程序(从 Unix 移植),它最初设计为在收到 ^C (Unix SIGINT) 时执行干净退出。在这种情况下,一个干净的退出涉及等待,可能相当长的时间,以关闭远程网络连接。 (我知道这不是 ^C 的正常行为,但我无法更改它。)该程序是单线程的。

我可以使用signal(SIGINT)(在Unix 下)或SetConsoleCtrlHandler 来捕获^C。当程序在 CMD.EXE 下运行时,两者都可以正常工作。但是,如果我使用 MSYS 附带的“bash”外壳(我使用 MinGW 环境来构建程序,因为这允许我重用 Unix makefile),那么程序会被强制终止一些随机的短时间(少于^C 之后的 100 毫秒)。这是不可接受的,因为正如我所提到的,程序需要等待远程网络连接关闭。

人们很可能希望在 MSYS bash 下运行这个程序。此外,这种效果会破坏测试套件。我无法从程序内部(理想)或通过 shell 上的设置(可接受)找到解决问题的任何方法。谁能推荐点什么?

【问题讨论】:

您可以在 Windows 上随意使用<signal.h>,但是当您在控制台窗口中键入 control-C 时,操作系统不会生成 SIGINT,因此它对您没有任何好处。 有没有办法让网络连接更健壮?即使你正确处理程序关闭,如果有人绊倒电源线会发生什么? <signal.h> 在 Windows 上使用 Microsoft CRT 为我工作。当我在控制台窗口中键入 Ctrl+C 时,我使用 signal() 注册的处理程序会被 SIGINT 调用。 @Brian:实际上,我认为这是我的错误:请参阅***.com/questions/7085604/… -- generating 来自另一个进程的 CTRL_C_EVENT 似乎在 kernel32 级别上不受支持,这让我觉得信号处理程序没有做任何建设性的事情。 @Zack 听起来你已经找到了答案——也许是时候结束这个问题了,这样像我这样的人就不会花时间试图回答它了? ;) 【参考方案1】:

我遇到了完全相同的问题 - 我编写了一个带有 SIGINT/SIGTERM 处理程序的程序。那个处理程序做了有时需要一段时间的清理工作。当我从 msys bash 中运行程序时, ctrl-c 会导致我的 SIGINT 处理程序触发,但它不会完成 - 程序在完成清理之前被终止(“从外部”,因为它是)工作。

基于 phs 的回答,以及对类似问题的回答:https://***.com/a/23678996/2494650,我想出了以下解决方案。它非常简单,并且可能会产生一些我尚未发现的副作用,但它为我解决了问题。

使用以下行创建一个 ~/.bashrc 文件:

trap '' SIGINT

就是这样。这会捕获 sigint 信号并防止 msys bash “从外部”终止您的程序。但是,它仍然以某种方式让 SIGINT 信号通过您的程序,允许它进行优雅的清理/关闭。我不能确切地告诉你为什么它会这样工作,但它确实 - 至少对我来说是这样。

祝你好运!

【讨论】:

我猜这可能与 Cygwin Bash 的信号处理程序干扰了中断的系统调用有关。我记得 POSIX 信号处理程序不应该执行任何系统调用以避免干扰。我不知道 Bash 如何在 Linux 中实现陷阱,但它似乎总是通过重置陷阱并将其设置为空处理程序来完成陷阱。示例脚本显示了陷阱的不稳定行为 - 在 Cygwin 中,当使用超时 2s(发送 SIGTERM)调用时,gist.github.com/ilatypov/2d8d8043ef6592ebd6064906b773c6c7 发现了一个副作用 - ^c 清空当前键入的命令不再有效。 (例如输入 asdf 然后 ^c) 仅供参考——这个技巧显然是works for 32-bit but not for 64-bit ...不知道为什么。【参考方案2】:

这可能是由于臭名昭著的mintty "Input/Output interaction with alien programs" 问题(又名mintty issue #56)。在这种情况下,它表现为 Ctrl-C 突然终止程序,而不是作为要捕获和处理的信号传递给程序。该理论的证据基于 zwol 的广泛解释:“控制台模式 Windows 应用程序”、“[应用程序] 设计为在收到 ^C 时执行干净退出”、“[应用程序] 正常工作当程序在 CMD.EXE 下运行时”但“[使用终端时] MSYS [...] 程序被强制终止”(在撰写本文时(2018 年)MSYS 默认使用 mintty 作为其终端) .

不幸的是mintty isn't a full Windows console replacement 和“本地”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 。

【讨论】:

【参考方案3】:

Arg - 5 分钟编辑评论。这是我想写的:

作为一种解决方法,我建议关闭标准输入上的 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 时设置一个事件来清除。

【讨论】:

我喜欢这个主意。我会在下次循环回到 Windows 问题时尝试它,并让你知道它是怎么回事。【参考方案4】:

当你用 MSYS bash 运行你的程序时,你是直接运行可执行文件,还是有一个包装(bash)的 shell 脚本?

如果是这样,它可能正在使用trap 命令注册一个自定义 Ctrl-C 处理程序(它会在睡眠后执行终止操作。)如果存在这种情况,请更改或删除它。

如果没有注册trap,或者没有包装脚本,请考虑制作这样的脚本并添加自己的陷阱来覆盖默认行为。您可以在here 或bash's man page 上查看如何使用它的示例(在 SHELL BUILTINS 部分)。

【讨论】:

我直接运行可执行文件。我想认为有一个比创建包装脚本更好的解决方案来处理这个 MSYS 错误(我怀疑它不会工作 - 请参阅问题中从 cmets 链接的错误报告;这是一个问题深入 MSYS 的内部)。【参考方案5】:

Ctrl-C 是 SIGINT?我以为 Ctrl-Z 是 SIGINT,但 Ctrl-C 是 SIGTERM。检查一下。

【讨论】:

嗯,从技术上讲,Windows 没有 SIGINT SIGTERM。 Ctrl-Z 在标准输入上生成 EOF,而 Ctrl-C 导致生成“控制台控制事件”。有两个记录在案的控制台控制事件:Ctrl-C 和 Ctrl-BREAK。 MSVCRT 似乎在内部将这两个映射到 SIGINT。见msdn.microsoft.com/en-us/library/ms683155%28v=VS.85%29.aspx 即使在 UNIX 上,Ctrl-C 通常发送 SIGINT,Ctrl-Z 通常发送 SIGTSTP(终端停止)。【参考方案6】:

您是否有 CYGWIN 环境设置(在控制面板/环境变量中)?尝试设置 CYGWIN=notty 并重新启动打开一个新的 MSYS bash shell - 问题是否仍然存在?

【讨论】:

我的错 - 在这种情况下,为什么不使用您自己的自定义键盘处理程序?我建议不要尝试捕获也被传播到 shell 的 CTRL-C 事件: 您能详细说明一下吗?我一点也不知道该怎么做。 嗨,扎克 - 在 cmets 中没有足够的位置 - 请参阅我关于在标准输入上禁用 CTRL-C 信号处理的答案。如果您需要一个简单的键盘处理程序示例,它将 CTRL-C 作为输入而不是信号,请告诉我,我会发布一个示例。

以上是关于防止 MSYS 'bash' 杀死捕获 ^C 的进程的主要内容,如果未能解决你的问题,请参考以下文章

bazel:使用 WSL 的 bash 而不是 MSYS64

怎么玩!在 MSYS bash 中运行的框架

在不退出的情况下运行 bash -c

从 C# Windows 窗体内部调用的 Bash 脚本

bash:git:在 MSYS 中找不到命令

为啥在 Windows7 MSYS 下安装 bash-it 时出现 mkdir 错误