【问题标题】:Custom Git command dies from signal 13 (SIGPIPE) after exec'ing git diff执行 git diff 后,自定义 Git 命令死于信号 13 (SIGPIPE)
【发布时间】:2016-01-31 17:38:29
【问题描述】:

如果您在 PATH 中创建一个名为 git-mydiff 的 shell 脚本,其中包含:

#!/bin/bash
exec git diff

并在有大量更改的存储库中调用git mydiff,当您退出分页器时,它会输出:

error: git-mydiff died of signal 13

但是,如果直接执行path/to/git-mydiff,退出pager时不会报错。

显然,一种解决方案是不使用exec,但为什么会出现问题?为什么只有通过git代理命令调用脚本时出现问题?

我正在使用:git 版本 2.5.4 (Apple Git-61)

【问题讨论】:

标签: git unix process signals exec


【解决方案1】:

您的程序,在本例中为mydiff,和寻呼机(less,或您选择的任何core.pager)通过管道连接。在读取器必须清除一些数据之前,操作系统对可以写入管道的数据量有一些限制,并且寻呼机在暂停之前不会读取整个管道,因此在某些输出量时,管道已填满并且您的程序在其write 系统调用中被阻止。

如果管道的读取端消失(通过让寻呼机退出),此时会发生两件事:操作系统向您的程序传递SIGPIPE 信号,并且操作系统有write 系统调用失败带有EPIPE 错误。通常,第一个(信号)甚至在第二个发生之前就杀死您的程序,但如果您的程序要捕获或忽略SIGPIPE,则会发生第二个。

下面是SIGPIPE 杀死进程的作业控制 shell 中的示例:

> cat book.pdf | : &
>
[1]    Broken pipe                   cat book.pdf |
       Done                          :

(顺便说一句,: 是内置的冒号命令,它是无操作的;我认为这是将 goto 作为外部程序的 Mashey shell 的剩余部分。)作为常规运行前台进程,序列是静默的:

> cat book.pdf | :
>

这是因为 shell 不会抱怨 dead-of-SIGPIPE 进程,因为“死于SIGPIPE”是很正常的。

无论出于何种原因,git 前端对于这个死于SIGPIPE 的案例更加嘈杂。如果你不使用exec,那么shell 会看到死的SIGPIPE。 shell 会安静地吸收它并干净地退出,git 不会抱怨。如果您确实使用exec,shell 将被您的程序替换,git 前端命令会看到 dead-of-SIGPIPE 状态并抱怨。

一个明显的治疗方法是留下外壳。另一种方法是让外壳忽略(不捕获)SIGPIPE,然后执行exec

trap "" PIPE

当你noted in a comment-reply捕捉 SIGPIPE 不好:因为exec 替换了地址空间的当前占用者,操作系统重置所有捕获信号到他们的默认处置(SIGPIPE 是“信号死亡”)。但是,ignored 信号仍然被忽略。

根据您的程序,这可能同样糟糕或更糟。例如,当cat 死于SIGPIPE 时,shell 是静默的,但是当cat 看到write 失败并返回EPIPE 时,它会抱怨:

$ cat book.pdf | :
$ trap "" PIPE
$ cat book.pdf | :
cat: stdout: Broken pipe

(这是在 FreeBSD 上;不同的操作系统略有不同,这取决于它们的实用程序有多仔细和聪明)。

【讨论】:

  • 感谢您的深入解释!如果我在exec git diff 之前将trap "" PIPE 添加到脚本中,当less 退出时,我仍然会看到“死于信号13”。它现在不应该因为EPIPE 而不是SIGPIPE 而失败吗?
  • 应该,是的(除非它正在做一些奇怪的事情,比如将信号显式重置为默认值)。转储正在进行的系统调用(trace、truss、ktrace、操作系统提供的任何东西......)可能会很有趣。
猜你喜欢
  • 2014-12-06
  • 1970-01-01
  • 1970-01-01
  • 2017-04-03
  • 2016-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-25
相关资源
最近更新 更多