os/signal

Package signal

  • import "os/signal"

  • Overview

  • Index

  • Examples

概观

封装信号实现对输入信号的访问。

信号主要用于类 Unix 系统。有关在 Windows 和 Plan 9上使用此软件包的信息,请参见下文。

Types of signals

信号 SIGKILL 和 SIGSTOP 可能不会被程序捕获,因此不会受此软件包影响。

同步信号是由程序执行中的错误触发的信号:SIGBUS,SIGFPE和SIGSEGV。这些只在程序执行时才被认为是同步的,而不是在使用 os.Process.Kill 或 kill 程序或类似的机制发送时。一般来说,除了如下所述,Go 程序会将同步信号转换为运行时恐慌。

其余信号是异步信号。它们不是由程序错误触发的,而是从内核或其他程序发送的。

在异步信号中,SIGHUP 信号在程序失去其控制终端时发送。当控制终端的用户按下中断字符(默认为^ C(Control-C))时,发送 SIGINT 信号。当控制终端的用户按下退出字符时发送 SIGQUIT 信号,默认为^ \(Control-Backslash)。一般情况下,您可以通过按^ C来使程序简单地退出,并且可以通过按^使堆栈转储退出。

Go程序中信号的默认行为

默认情况下,同步信号被转换为运行时恐慌。SIGHUP,SIGINT或SIGTERM信号导致程序退出。SIGQUIT,SIGILL,SIGTRAP,SIGABRT,SIGSTKFLT,SIGEMT或SIGSYS信号会导致程序以堆栈转储退出。SIGTSTP,SIGTTIN或SIGTTOU信号获取系统默认行为(这些信号由外壳用于作业控制)。SIGPROF信号由 Go 运行时直接处理以实现 runtime.CPUProfile 。其他信号将被捕获,但不会采取任何行动。

如果 Go 程序以忽略 SIGHUP 或 SIGINT(信号处理程序设置为 SIG_IGN)启动,它们将保持忽略。

如果 Go 程序是以非空信号掩码开始的,那么通常将会受到尊重。然而,一些信号被明确地解除阻碍:在GNU / Linux上,同步信号,SIGILL,SIGTRAP,SIGSTKFLT,SIGCHLD,SIGPROF和信号32(SIGCANCEL)和33(SIGSETXID)(SIGCANCEL和SIGSETXID由glibc内部使用)。由 os.Exec 或 os / exec包启动的子进程将继承修改的信号掩码。

改变Go程序中的信号行为

这个包中的函数允许程序改变 Go 程序处理信号的方式。

通知会禁用给定的一组异步信号的默认行为,而是通过一个或多个注册的通道传递它们。具体来说,它适用于信号SIGHUP,SIGINT,SIGQUIT,SIGABRT和SIGTERM。它也适用于作业控制信号SIGTSTP,SIGTTIN和SIGTTOU,在这种情况下系统默认行为不会发生。它也适用于一些不会引起任何动作的信号:SIGUSR1,SIGUSR2,SIGPIPE,SIGALRM,SIGCHLD,SIGCONT,SIGURG,SIGXCPU,SIGXFSZ,SIGVTALRM,SIGWINCH,SIGIO,SIGPWR,SIGSYS,SIGINFO,SIGTHR,SIGWAITING,SIGLWP,SIGFREEZE, SIGTHAW,SIGLOST,SIGXRES,SIGJVM1,SIGJVM2以及系统中使用的任何实时信号。请注意,并非所有这些信号都适用于所有系统。

如果程序在忽略 SIGHUP 或 SIGINT 的情况下启动,并且任何一个信号都会调用 Notify,则将为该信号安装一个信号处理程序,并且不会再被忽略。如果稍后为该信号调用 Reset 或 Ignore,或者在传递给 Notify 的所有通道上调用 Stop 以获取该信号,则信号将再次被忽略。重置将恢复信号的系统默认行为,而忽略会导致系统完全忽略信号。

如果程序以非空信号掩码开始,则一些信号将如上所述被明确地解除阻塞。如果 Notify 被呼叫阻塞信号,它将被解除阻塞。如果稍后对该信号调用 Reset,或者在传递给 Notify 的所有通道上调用 Stop,则该信号将再次被阻止。

SIGPIPE

当一个 Go 程序写入一个损坏的管道时,内核将产生一个 SIGPIPE 信号。

如果程序尚未调用 Notify 来接收 SIGPIPE 信号,则行为取决于文件描述符编号。在文件描述符1或2(标准输出或标准错误)上写入损坏的管道将导致程序以 SIGPIPE 信号退出。在某些其他文件描述符上写入损坏的管道将不会对 SIGPIPE 信号采取任何操作,并且写入操作将失败并显示 EPIPE 错误。

如果程序调用 Notify 来接收 SIGPIPE 信号,则文件描述符号码无关紧要。SIGPIPE 信号将被传送到通知通道,并且写入将失败并出现 EPIPE 错误。

这意味着,默认情况下,命令行程序的行为与典型的 Unix 命令行程序相同,而其他程序在写入封闭网络连接时不会因 SIGPIPE 而崩溃。

转到使用cgo或SWIG的程序

在包含非 Go 代码(通常使用cgo或SWIG访问的C / C ++代码)的Go程序中,Go 的启动代码通常首先运行。它在非运行启动代码运行之前,按照 Go 运行时的预期配置信号处理程序。如果 non-Go 启动代码希望安装自己的信号处理程序,则必须采取一定步骤才能保证Go运行正常。本节介绍这些步骤,以及 Go-Go 程序对信号处理器设置的整体效果变化。在极少数情况下,非 Go 代码可能在 Go 代码之前运行,在这种情况下,下一节也适用。

如果 Go 程序调用的非 Go 代码不会更改任何信号处理程序或掩码,则行为与纯 Go 程序的行为相同。

如果 non-Go 代码安装任何信号处理程序,则必须在 sigaction 中使用 SA_ONSTACK 标志。如果没有收到信号,很可能会导致程序崩溃。Go 程序通常使用有限的堆栈运行,因此设置了一个备用信号堆栈。另外,Go 标准库期望任何信号处理程序都将使用 SA_RESTART 标志。否则可能会导致某些库调用返回“中断的系统调用”错误。

如果 non-Go 代码为任何同步信号(SIGBUS,SIGFPE,SIGSEGV)安装信号处理程序,则应该记录现有的 Go 信号处理程序。如果这些信号在执行 Go 代码时发生,它应该调用 Go 信号处理程序(无论执行 Go 信号时发生的信号是否可以通过查看传递给信号处理程序的 PC 来确定)。否则,某些 Go 运行时恐慌不会按预期发生。

如果 non-Go 代码为任何异步信号安装信号处理程序,则它可以调用Go信号处理程序或不按照它选择的方式。当然,如果它不调用 Go 信号处理程序,则上述 Go 行为不会发生。这可能是 SIGPROF 信号的问题。

非 Go 代码不应该改变 Go 运行时创建的任何线程上的信号掩码。如果 non-Go 代码自己启动新线程,它可以根据需要设置信号掩码。

如果 non-Go 代码启动一个新线程,更改信号掩码,然后调用该线程中的 Go 函数,则 Go 运行时将自动解除某些信号的阻塞:同步信号SIGILL SIGTRAP SIGSTKFLT SIGCHLD SIGPROF SIGCANCEL和SIGSETXID。当 Go 函数返回时,非 Go 信号掩码将被恢复。

如果 Go 信号处理程序是在未运行 Go 代码的非 Go 线程上调用的,则处理程序通常会将该信号转发给非 Go 代码,如下所示。如果信号是 SIGPROF,则 Go 处理程序不执行任何操作。否则,Go 处理程序会自行删除,解除阻塞信号并再次提升,以调用任何非 Go 处理程序或缺省系统处理程序。如果程序没有退出, Go 处理程序会自行重新安装并继续执行程序。

非Go程序调用Go代码

当 Go 代码使用诸如-buildmode = c-shared之类的选项构建时,它将作为现有非 Go 程序的一部分运行。非 Go 代码在 Go 代码开始时可能已经安装了信号处理程序(当使用 cgo 或 SWIG 时,这种情况也会发生在特殊情况下;在这种情况下,这里的讨论适用)。对于-buildmode = c-archive,Go 运行时将在全局构造函数时初始化信号。对于-buildmode = c-shared,Go 运行时将在共享库加载时初始化信号。

如果 Go 运行时发现 SIGCANCEL 或 SIGSETXID 信号(仅用于GNU / Linux)的现有信号处理程序,它将打开 SA_ONSTACK 标志,否则保持信号处理程序。

对于同步信号和 SIGPIPE,Go 运行时将安装一个信号处理程序。它将保存任何现有的信号处理程序。如果同步信号在执行非 Go 代码时到达,Go 运行时将调用现有的信号处理程序而不是 Go 信号处理程序。

使用-buildmode = c-archive或-buildmode = c-shared构建的Go代码默认情况下不会安装任何其他信号处理程序。如果存在现有的信号处理程序,Go 运行时将打开 SA_ONSTACK 标志,否则保持信号处理程序。如果 Notify 被调用为异步信号,则会为该信号安装 Go 信号处理程序。如果稍后对该信号调用 Reset,则将重新安装该信号的原始处理,如果有的话还原非 Go 信号处理程序。

不使用-buildmode = c-archive或-buildmode = c-shared构建的 Go 代码将为上面列出的异步信号安装信号处理程序,并保存任何现有的信号处理程序。如果一个信号被传送到一个非 Go 线程,它将按照上面描述的方式工作,除了如果有一个现有的非 Go 信号处理程序,该处理程序将在提升信号之前被安装。

Windows

在 Windows 上,^ C(Control-C)或^ BREAK(Control-Break)通常会导致程序退出。如果通知被调用 os.Interrupt , ^ C 或 ^ BREAK 将导致 os.Interrupt 在通道上发送,并且程序不会退出。如果调用Reset,或在传递给 Notify 的所有通道上调用 Stop,则将恢复默认行为。

Plan 9

在计划9中,信号具有类型 syscall.Note,这是一个字符串。使用 syscall.Note 调用通知将导致该值在通道上发送,当该字符串发布为备注时。

Index

  • func Ignore(sig ...os.Signal)

  • func Notify(c chan<- os.Signal, sig ...os.Signal)

  • func Reset(sig ...os.Signal)

  • func Stop(c chan<- os.Signal)

例子

Notify

包文件

doc.go signal.go signal_unix.go

func Ignore(显示源文件)

func Ignore(sig ...os.Signal)

忽略导致提供的信号被忽略。如果他们被程序收到,什么都不会发生。忽略任何先前通知提供的信号通知的效果。如果没有提供信号,则所有输入信号都将被忽略。

func Notify(显示源文件)

func Notify(c chan<- os.Signal, sig ...os.Signal)

通知会导致软件包信号将输入信号中继到 c 。如果没有提供信号,所有输入信号将被中继到 c 。否则,只有提供的信号会。

包信号不会阻止发送到 c:调用者必须确保 c 有足够的缓冲空间来跟上预期的信号速率。对于仅用于通知一个信号值的通道,大小为1的缓冲区就足够了。

允许使用相同的频道多次调用通知:每个呼叫都会扩展发送到该频道的一组信号。从集合中删除信号的唯一方法是调用 Stop 。

可以使用不同的通道和相同的信号多次呼叫通知:每个通道独立接收输入信号的副本。

package main import ( "fmt" "os" "os/signal" ) func main() { // Set up channel on which to send signal notifications. // We must use a buffered channel or risk missing the signal // if we're not ready to receive when the signal is sent. c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) // Block until a signal is received. s := <-c fmt.Println("Got signal:", s) }

func Reset(显示源文件)

func Reset(sig ...os.Signal)

重设撤消任何先前呼叫的效果,以通知所提供的信号。如果没有提供信号,所有信号处理程序将被重置。

func Stop(显示源文件)

func Stop(c chan<- os.Signal)

停止导致封装信号停止将输入信号中继到 c 。它解除了以前使用 c 通知通知的效果。当停止返回时,保证 c 将不会收到更多信号。