为什么即使有 nohup 也会捕获挂断信号?
Why hangup signal is caught even with nohup?
package main
import (
"os"
"os/signal"
log "github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
func main() {
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, unix.SIGHUP)
go func() {
s := <-sigs
log.Info("OS signal: " + s.String())
}()
DoSomething()
}
我编译了上面的 Go 代码并使用以下命令执行:
nohup ./build_linux > /dev/null 2>&1 &
但是当我退出终端时进程仍然捕捉到 HANGUP 信号。
好像signal.Notify
优先级高,nohup
指令无效
这是怎么回事,为什么 nohup
不能阻止向进程发送挂断信号?
TL;DR
先检查signal.Ignored()
:
if !signal.Ignored(unix.SIGHUP) {
signal.Notify(sigs, unix.SIGHUP)
}
长
的正确答案是:运行:
nohup ./build_linux
从命令行启动 ./build_linux
程序,其中 SIGHUP
设置为 SIG_IGN
(所有这些信号名称都是通用的 Linux 名称,而不是 Go 包名称).如果您为 SIGHUP
安装自己的处理程序,这将覆盖 SIGHUP
的当前设置。
一般来说,在Unix/Linux程序中,正确的模式是测试信号是否当前被忽略之前(或者作为一部分)安装 signal-catch 函数。如果信号被忽略,将其恢复为被忽略。
为了使这个过程完全可靠,最有效的正确模式是:
- 延迟信号(可能是所有信号);
- 安装任何需要的处理程序,return是信号的当前配置;
- 如果当前配置为
SIG_IGN
,return 则配置为 SIG_IGN
;
- 解除信号
holding-and-releasing 是通过 Unix/Linux sigprocmask
或 pthread_sigmask
系统调用完成的。使用哪一个取决于您是否使用线程。 Go 当然使用线程;参见,例如 this patch to the Cgo runtime startup from 2013 (fixes issue #6811).
自从引入 signal.Ignored
的 Go 1.11 以来,您可以直接使用它,因为 Go 运行时已经在启动时完成了所有适当的 hold / set-and-test / restore 序列,并缓存了结果.应该 绝对 将此用于 SIGHUP
以遵守 nohup
约定。人们通常也应该将它用于 SIGINT
和其他键盘信号,并且几乎1 没有理由 不 将它用于所有信号。1
1Jenkins,或者至少是 Jenkins 的某个版本,显然(错误地)将所有信号设置为在 运行 测试套件启动时忽略。
package main
import (
"os"
"os/signal"
log "github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
func main() {
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, unix.SIGHUP)
go func() {
s := <-sigs
log.Info("OS signal: " + s.String())
}()
DoSomething()
}
我编译了上面的 Go 代码并使用以下命令执行:
nohup ./build_linux > /dev/null 2>&1 &
但是当我退出终端时进程仍然捕捉到 HANGUP 信号。
好像signal.Notify
优先级高,nohup
指令无效
这是怎么回事,为什么 nohup
不能阻止向进程发送挂断信号?
TL;DR
先检查signal.Ignored()
:
if !signal.Ignored(unix.SIGHUP) {
signal.Notify(sigs, unix.SIGHUP)
}
长
nohup ./build_linux
从命令行启动 ./build_linux
程序,其中 SIGHUP
设置为 SIG_IGN
(所有这些信号名称都是通用的 Linux 名称,而不是 Go 包名称).如果您为 SIGHUP
安装自己的处理程序,这将覆盖 SIGHUP
的当前设置。
一般来说,在Unix/Linux程序中,正确的模式是测试信号是否当前被忽略之前(或者作为一部分)安装 signal-catch 函数。如果信号被忽略,将其恢复为被忽略。
为了使这个过程完全可靠,最有效的正确模式是:
- 延迟信号(可能是所有信号);
- 安装任何需要的处理程序,return是信号的当前配置;
- 如果当前配置为
SIG_IGN
,return 则配置为SIG_IGN
; - 解除信号
holding-and-releasing 是通过 Unix/Linux sigprocmask
或 pthread_sigmask
系统调用完成的。使用哪一个取决于您是否使用线程。 Go 当然使用线程;参见,例如 this patch to the Cgo runtime startup from 2013 (fixes issue #6811).
自从引入 signal.Ignored
的 Go 1.11 以来,您可以直接使用它,因为 Go 运行时已经在启动时完成了所有适当的 hold / set-and-test / restore 序列,并缓存了结果.应该 绝对 将此用于 SIGHUP
以遵守 nohup
约定。人们通常也应该将它用于 SIGINT
和其他键盘信号,并且几乎1 没有理由 不 将它用于所有信号。1
1Jenkins,或者至少是 Jenkins 的某个版本,显然(错误地)将所有信号设置为在 运行 测试套件启动时忽略。