在 SIGINT 上,Linux 上的退出代码为 130,Windows 上的退出代码为 2

Gab*_*iel 1 exit-code go sigint

为什么下面的空程序在 Linux 上以 130 退出CtrlC这是我怀疑的,因为我的 shellbash将 SIGINT 包装为 130 (128+2)。

在使用Git Bash ( )的 Windows 上git-bash.exe,我得到退出代码 2。

package main

func main() {
    for {

    }
}
Run Code Online (Sandbox Code Playgroud)

这是 Go 在 Windows 上的行为还是git-bash.exe?因为我2内部需要退出代码,所以我需要使用signal包来包装它吗?

kos*_*tix 6

嗯,它有两重。

\n

一方面,正如@Flimzy 指出的那样,它是 shell 的干预。

\n

另一方面,他的言论中缺少的是为什么会发生这种情况。
\n同样,解释有两个方面:

\n
    \n
  • 进程具有某些默认的信号处理配置,即进程对某些信号的反应方式。信号可以被忽略、处理或保持原样,在大多数情况下(如果不是全部),这意味着杀死进程。\n您可以在此处
    阅读有关此内容的更多信息。\n请注意,在重要的进程中,例如具有复杂运行时系统的用 Go 编写的程序,默认信号处理可能与“更简单”的进程不同。\n默认情况下,信号不被处理,这意味着它会终止进程。

    SIGINT
  • \n
  • 如果进程被致命信号杀死,GNU Bash shell 会在进程的退出代码中添加 128。有关此 \xe2\x80\x94 的更多信息,请参阅bash 手册
  • \n
\n
\n

更新Windows 中的行为。

\n

让我们首先提供一个快速的情况说明书:

\n
    \n
  • Windows 根本不支持 Unix 信号的概念。
  • \n
  • 终端感知程序在类 Unix 系统上的工作方式与控制台感知程序在 Windows 上的工作方式非常不同。
    \n比如说,Vim 在 Windows 上的“Git bash”窗口中的外观和行为方式可能看起来与在基于 Linux 的操作系统上的 GNOME 终端窗口中的外观非常相似,但潜在的差异是深刻的。
  • \n
\n

现在让我们更深入地挖掘一下。

\n

Unix 诞生时没有任何 GUI 的概念,用户将使用硬件终端与 Unix 系统交互。\n为了支持它们,类 Unix 操作系统的内核实现了特殊的标准化方式,使终端感知程序与系统交互;如果您“想”深入研究技术细节,我强烈建议您阅读“TTY 揭秘”文章。
\n这种方法的两个更重要的亮点是:

\n
    \n
  • 终端子系统甚至被运行在 Windows 中的当代新生称为“终端”\xe2\x80\x94 的程序使用,这些程序通常首先运行 shell,在其中调用各种命令行程序,包括那些使用“full”的程序。 screen"\xe2\x80\x94等文本编辑器。\n这基本上意味着,如果你使用 Vim 或 GNU Nano,它可以在任何图形终端模拟器中运行得很好,或者直接在 Linux 的“虚拟终端”上运行(你可以通过点击- -
    在 PC 上获得这些文本屏幕)或在 GUI 关闭的情况下启动)或在连接到计算机的硬件终端上。CtrlAltF1
  • \n
  • 终端子系统分配键盘可能发送给它的某些代码以执行某些操作\xe2\x80\x94,而不是将这些编码器直接发送到连接到该终端的程序,并且Ctrl-C是其中之一:在常见的默认设置中按组合键使终端子系统向前台进程发送SIGINTUnix信号。
  • \n
\n

后者特别令人感兴趣。您可以stty -a在 Linux 系统的终端窗口中运行;在大量的输出中,您会看到类似的内容,intr = ^C; quit = ^\\;这意味着Ctrl-C发送交互主动注意 ( ) 信号并且-发送(是的,“SIGINT”中的“INT”并不代表“中断”\xe2\x80\x94,与一种流行的信念)。\n您几乎可以随意重新分配这些组合键(尽管按照许多软件所期望的方式进行映射并按照它们通常的方式进行映射并且不将自己的操作分配给这些手势并不是明智之举\xe2\ x80\x94 理所当然地期望无法真正收到它们。SIGINTCtrl\\SIGQUIT
^C^\\

\n

现在回到Windows。
\n在 Windows 上,没有终端子系统,也没有信号。\nWindows 上的控制台窗口是提供与旧 MS-DOS 系统兼容性所需的人工制品,情况如下:Ctrl-Break会触发通常处理的硬件中断由操作系统,并且Ctrl-C可以明确启用以执行相同的操作。Windows 上控制台模拟的实现仔细地模拟了这种行为,但由于 Windows 没有类似 Unix 的信号,因此这些键盘组合的处理方式有所不同\xe2\x80\x94,但效果大致相同

\n
\n

每个控制台进程都有自己的应用程序定义的处理和信号HandlerRoutine函数列表。当用户关闭控制台、注销或关闭系统时,处理程序函数还处理系统生成的信号。最初,每个进程的处理程序列表仅包含调用该函数的默认处理程序函数。CTRL+CCTRL+BREAKExitProcess

\n
\n

这一切对 Go 意味着什么?

\n

让我们首先看一下文档:

\n
\n

~$ GOOS=windows go doc os.Interrupt

\n
package os // import "os"\n\nvar (\n  Interrupt Signal = syscall.SIGINT\n  Kill      Signal = syscall.SIGKILL\n)\n
Run Code Online (Sandbox Code Playgroud)\n

所有系统上 os 包中保证存在的唯一信号值是os.Interrupt(向进程发送中断)和os.Kill(强制进程退出)。在 Windows 上,未实现发送os.Interrupt到进程;os.Process.Signal它将返回错误而不是发送信号。

\n
\n

因此,在 Windows 上运行的 Go 程序中,您可以处理这两个“信号”\xe2\x80\x94,即使它们并未真正实现为信号。

\n

现在让我们开始解释退出代码的差异。

\n

正如您现在所知,Ctrl-C当程序在类 Unix 系统上的终端仿真器窗口中运行时按 将使终端子系统向进程发送实际SIGINT信号。\n如果未显式处理此信号,则进程会被操作系统(默认信号处理就是这样)。\nshell 注意到它生成的进程突然死亡,收集其退出代码并向其添加 128(因为它不希望它以这种方式死亡) 。
\n在 Windows 上,点击Ctrl-C使进程执行系统ExitProcess调用,从 shell 进程的角度来看,这看起来像正常的进程退出:它无法区分此退出与进程os.Exit(0)显式调用时发生的退出。

\n

  • @Gabriel,可能是因为 Windows 没有信号,所以 shell 不会参与检测致命信号的含义。 (3认同)