在linux环境中除了程序本身运行结束退出外,还有很多其他的退出方式,比如我们在shell里面直接执行命令kill或者kill -9.仅仅是多了一个-9参数就属于不同的退出方式了,因为他们将会给程序发送不同的信号量.
当我们执行kill的时候,默认发送SIGTERM信号到程序中,当程序接收到这个信号的时候,可能会出现以下集中方式:
- 退出程序
- 执行一些清理操作后退出
- 忽略掉,继续执行
这三种方式都是程序中可以通过代码直接实现的,比较好的执行方式就是通过第二种进行一些资源的清理后再去退出,而有些程序则不会像我们想象的那样执行退出操作,所以管理员一旦发现这种情况,往往会执行更加直接的方式,发送一个-9参数的信号到程序中,也就是SIGKILL信号,这个信号是不能被直接忽略的,他会导致整个系统的init程序停止这个进程的运行,进程直接退出,而不会有任何的机会去意识到这个行为的发生.
另外一种就是我们对于前台运行的程序的终止,我们一般会执行一个ctrl-c来退出程序的执行,这个组合键会发送给程序一个SIGINT指令,该指令也可以在程序中捕获,所以我们知道了应该捕获什么样的信号指令来进行预定义的退出环节.
下面我们使用的代码就是利用了signal.Notify函数来绑定两种信号到管道中,一旦接收到该信号就传递他们到channel中,另外我们还有一个专门的goroutine来消费这个channel的数据,默认情况下阻塞在第一条语句,一旦有信号传递进来,执行清理,并退出系统.代码如下:
package main
import (
"log"
"os"
"os/signal"
"sync"
"syscall"
"time"
)
func main() {
var stopLock sync.Mutex
stop := false
stopChan := make(chan struct{}, 1)
signalChan := make(chan os.Signal, 1)
go func() {
//阻塞程序运行,直到收到终止的信号
<-signalChan
stopLock.Lock()
stop = true
stopLock.Unlock()
log.Println("Cleaning before stop...")
stopChan <- struct{}{}
os.Exit(0)
}()
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
//模拟一个持续运行的进程
time.Sleep(10 * time.Second)
}