Linux 上的退出代码 130 和 SIGINT 上 Windows 上的退出代码 2

问题描述

为什么以下空程序在 Linux 上以 130 退出 Ctrl+C(这是我怀疑的,因为我的 shell bash 将 SIGINT 包装为 130 (128+2)。 在带有 git-bash.exe 的 Windows 上,我得到退出代码 2。

package main

func main() {
    for {

    }
}

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

解决方法

嗯,这是双重的。

一方面,正如@Flimzy 所指出的,它是外壳干预。

另一方面,他的话中缺少的是为什么会发生这种情况。
再次说明,有两个方面:

  • 进程具有特定的默认信号处理处置,这是进程对特定信号的反应方式。信号可以被忽略、处理或保持原样,这在大多数情况下(如果不是全部)意味着终止进程。
    您可以阅读有关此 here 的更多信息。
    请注意,在非平凡的进程中,例如用 Go 编写的程序具有复杂的运行时系统,默认信号处理可能与“更简单”的进程不同。
    默认情况下,不会处理 SIGINT 信号,这意味着它会终止进程。
  • 如果进程被致命信号杀死,她会在 bash 中将 128 添加到进程的退出代码中。更多相关信息 - 在 bash manual 中。

更新 Windows 中的行为。

让我们先做一个速览:

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

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

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

  • 终端子系统甚至被当代新生称之为“终端”的程序所使用——在通常开始运行 shell 的 Windows 中,您可以在其中调用各种命令行程序,包括那些使用“全屏”的程序"——比如文本编辑器。
    这基本上意味着,如果你使用 Vim 或 GNU Nano,它可以在任何图形终端模拟器中正常运行,或者直接在 Linux 的“虚拟终端”上运行(你可以通过点击 Ctrl-Alt-F1 或在 GUI 关闭的情况下启动)或在连接到计算机的硬件终端上。
  • 终端子系统分配某些代码键盘可能会发送给它以执行某些操作 - 而不是将这些编码器直接发送到连接到该终端的程序,然后 Ctrl-C 就是其中之一:在常见的默认设置中,按下该组合键会使终端子系统向前台进程发送 SIGINT Unix 信号。

后者特别有趣。您可以在 Linux 系统的终端窗口中运行 stty -a;在大量输出中,您会看到类似 intr = ^C; quit = ^\; 的内容,这意味着 Ctrl-C 发送 interactive attention (SIGINT) 信号和 Ctrl-\ 发送 SIGQUIT(是的,“SIGINT”中的“INT”不代表“中断”——与流行的看法相反)。
您几乎可以随意重新分配这些键组合(尽管这样做并不是明智之举,因为许多软件都希望 ^C^\ 以它们通常的方式进行映射并且不分配自己的操作对这些手势 - 理所当然地期望无法实际接收它们。

现在回到 Windows。
在 Windows 上,没有终端子系统,也没有信号。 Windows 上的控制台窗口是提供与旧 MS-DOS 系统兼容所需的人工制品,情况是这样的:Ctrl-Break 通常会触发硬件中断由操作系统处理,并且可以显式启用 Ctrl-C 以执行相同的操作。 Windows 上控制台模拟的实现仔细地模拟了这种行为,但由于 Windows 没有类 Unix 信号,这些键盘组合的处理方式有所不同——尽管 with much the same effect:

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

这对 Go 意味着什么?

让我们先看看文档:

~$ GOOS=windows go doc os.Interrupt

package os // import "os"

var (
  Interrupt Signal = syscall.SIGINT
  Kill      Signal = syscall.SIGKILL
)

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

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

现在让我们开始解释退出代码的区别。

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

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...