一,信号类型

man 7 signal

在POSIX.1-1990标准中定义的信号列表

信号动作说明
SIGHUP1Term终端控制进程结束(终端连接断开)
SIGINT2Term用户发送INTR字符(Ctrl+C)触发
SIGQUIT3Core用户发送QUIT字符(Ctrl+/)触发
SIGILL4Core非法指令(程序错误、试图执行数据段、栈溢出等)
SIGABRT6Core调用abort函数触发
SIGFPE8Core算术运行错误(浮点运算错误、除数为零等)
SIGKILL9Term无条件结束程序(不能被捕获、阻塞或忽略)
SIGSEGV11Core无效内存引用(试图访问不属于自己的内存空间、对只读内存空间进行写操作)
SIGPIPE13Term消息管道损坏(FIFO/Socket通信时,管道未打开而进行写操作)
SIGALRM14Term时钟定时信号
SIGTERM15Term结束程序(可以被捕获、阻塞或忽略)
SIGUSR130,10,16Term用户保留
SIGUSR231,12,17Term用户保留
SIGCHLD20,17,18Ign子进程结束(由父进程接收)
SIGCONT19,18,25Cont继续执行已经停止的进程(不能被阻塞)
SIGSTOP17,19,23Stop停止进程(不能被捕获、阻塞或忽略)
SIGTSTP18,20,24Stop停止进程(可以被捕获、阻塞或忽略)
SIGTTIN21,21,26Stop后台程序从终端中读取数据时触发
SIGTTOU22,22,27Stop后台程序向终端中写数据时触发

在SUSv2和POSIX.1-2001标准中的信号列表:

信号动作说明
SIGTRAP5CoreTrap指令触发(如断点,在调试器中使用)
SIGBUS0,7,10Core非法地址(内存地址对齐错误)
SIGPOLL TermPollable event (Sys V). Synonym for SIGIO
SIGPROF27,27,29Term性能时钟信号(包含系统调用时间和进程占用CPU的时间)
SIGSYS12,31,12Core无效的系统调用(SVr4)
SIGURG16,23,21Ign有紧急数据到达Socket(4.2BSD)
SIGVTALRM26,26,28Term虚拟时钟信号(进程占用CPU的时间)(4.2BSD)
SIGXCPU24,24,30Core超过CPU时间资源限制(4.2BSD)
SIGXFSZ25,25,31Core超过文件大小资源限制(4.2BSD)

 

二,golang通过协程处理指定的信号

package main
 
import (
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"
)
 
func main() {
	// start goroutine to listen some signal
	go signalListen()
	// main loop
	for {
		time.Sleep(30 * time.Second)
		fmt.Println("main loop.")
	}
}
 
func signalListen() {
	// init os.signal channel
	c := make(chan os.Signal)
	// define catch signal
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	for {
		// wait channel
		sig := <-c
		// when receive signal,then notify channel,and print the follow info.
		fmt.Println("receive signal:", sig)
	}
}

 

三,如何通过信号退出后台demo

package main
 
import (
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"
)
 
//创建监听退出chan
var c = make(chan os.Signal)
 
func main()  {
 
	//监听指定信号 ctrl+c kill
	signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGUSR1, syscall.SIGUSR2)
	go signalProcess()
 
	fmt.Println("start...")
	num := 0
	for {
		num++
		fmt.Println("seconds : ", num)
		time.Sleep(time.Second)
	}
}
 
func signalProcess() {
	for s := range c {
		switch s {
		case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT:
			fmt.Println("exit signal: ", s)
			ExitFunc()
		case syscall.SIGUSR1:
			fmt.Println("usr1", s)
		case syscall.SIGUSR2:
			fmt.Println("usr2", s)
		default:
			fmt.Println("other", s)
		}
	}
	return
}
 
func ExitFunc()  {
	fmt.Println("begin exit!!!")
	fmt.Println("clean!!!")
	fmt.Println("end exit!!!")
	os.Exit(0)
}

上面的例子可以应用于,demo通过startProcess启动了很多子进程,当前程序如果退出,不管理这些子进程,那么他们将成为孤儿进程被init进程接管,如果这些进程,占用了公共资源,比如端口,那么下次启动demo,端口将被占用,无法正常使用。那么我们就可以通过此方法来监听信号,尽可能的避免这种情况产生。

尽管如此,还有一些情况是我们无法处理的,比如:SIGKILL,SIGSTOP这两个信号,是无法被捕捉到的,也就是说,如果通过这两个信号结束程序,我们还是无法处理善后工作。