信号是IPC之一,下面是常用的信号列表
信号 | 值 | 动作 | 说明 |
---|---|---|---|
SIGHUP | 1 | Term | 终端控制进程结束(终端连接断开) |
SIGINT | 2 | Term | 用户发送INTR字符(Ctrl+C)触发 |
SIGQUIT | 3 | Core | 用户发送QUIT字符(Ctrl+/)触发 |
SIGILL | 4 | Core | 非法指令(程序错误、试图执行数据段、栈溢出等) |
SIGABRT | 6 | Core | 调用abort函数触发 |
SIGFPE | 8 | Core | 算术运行错误(浮点运算错误、除数为零等) |
SIGKILL | 9 | Term | 无条件结束程序(不能被捕获、阻塞或忽略) |
SIGSEGV | 11 | Core | 无效内存引用(试图访问不属于自己的内存空间、对只读内存空间进行写操作) |
SIGPIPE | 13 | Term | 消息管道损坏(FIFO/Socket通信时,管道未打开而进行写操作) |
SIGTERM | 15 | Term | 结束程序(可以被捕获、阻塞或忽略) |
SIGUSR1 | 30,10,16 | Term | 用户保留 |
SIGUSR2 | 31,12,17 | Term | 用户保留 |
SIGCHLD | 20,17,18 | Ign | 子进程结束(由父进程接收) |
SIGTSTP | 18,20,24 | Stop | 停止进程(可以被捕获、阻塞或忽略) |
我们已经知道信号是IPC方式之一,那么在go中,我们就可以利用信号来优化和完善我们的程序。
熟悉nginx的可以知道 nginx -s 其他命令
其实就是利用信号来实现master和worker 进程间的通信。我们也可以模仿nginx来实现go进程通信
场景 1 ,以非守护进程的方式运行go 程序
go run main.go
当我们执行 Ctrl+C
动作时,会触发SIGINT 信号,我们可以捕获这一信号来进行相应的处理。
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
)
func main() {
c := make(chan os.Signal)
stop := make(chan int)
signal.Notify(c, syscall.SIGINT)
go func() {
for {
select {
case s := <-c:
fmt.Println(fmt.Sprintf("捕获到SIGINT:%s",s))
//do something
os.Exit(0)
}
}
}()
<-stop
}
执行 Ctrl+C
output : ^C捕获到SIGINT:interrupt
场景 2 ,以守护进程的方式运行go 程序
go run main.go &
注意:执行 kill -9 pid 时,SIGKILL信号是不能被捕获的
假设该程序的PID为233333,我们可以学习PHP的fmp优雅重启来捕获SIGUSR2 信号来自定义后面的重启处理
注意:SIGUSR2 在DARWIN 下 值为31,LINUX_AMD64 为 12
step1: go run main.go &
step2: ps -ef | grep 'go run main.go'
step3: kill -31 PID
如上就是一个乞丐版平滑重载配置
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
)
func main() {
c := make(chan os.Signal)
stop := make(chan int)
signal.Notify(c, syscall.SIGUSR2)
go func() {
for {
select {
case s := <-c:
fmt.Println(fmt.Sprintf("捕获到SIGUSR2,正在重启配置:%s",s))
//do something
fmt.Println("我已经重新加载了配置")
os.Exit(0)
}
}
}()
<-stop
}
有疑问加站长微信联系(非本文作者)