正常关闭
- 系统关机:操作系统会自动关闭进程?
异常关闭:
- kill -9
- Runtime.halt()
- 断电
- 系统crash
kill -9 和 kill -15 有什么区别?
kill -15 pid的作用是向进程号为pid的进程发送SIGTERM(kill默认发送的信号ID就是15,所以可以简写成kill pid),该信号是一个结束进程的信号且可以被应用程序捕获,若应用程序没有捕获并响应该信号的逻辑代码,则该信号的默认动作是kill掉进程(等价于-9)。kill pid是终止指定进程的推荐做法。
kill -9 pid则是向进程号为pid的进程发送SIGKILL(该信号的编号为9),SIGKILL既不能被应用程序捕获,也不能被阻塞或忽略,其动作是立即结束指定进程。通俗地说,应用程序根本无法“感知”SIGKILL信号,它在完全无准备的情况下,就被收到SIGKILL信号的操作系统给干掉了,可以认为对于被kill的进程来说,kill -9 pid模拟了一次系统宕机(断电)。
显然,在这种“暴力”情况下,应用程序完全没有释放当前占用资源的机会,一般不推荐作为进程终止的方式。事实上,SIGKILL信号是直接发给init进程的,它收到该信号后,负责终止pid指定的进程。通常在某些异常情况下(如进程已经hang死,无法响应正常信号)会使用kill -9来结束进程。
若通过kill结束的进程是一个创建过子进程的父进程,则其子进程就会成为孤儿进程(Orphan Process),这种情况下,子进程的退出状态就不能再被应用进程捕获(因为作为父进程的应用程序已经不存在了),而是交由init进程(1号进程)代为捕获和清理,通常也不会对整个linux系统产生什么不利影响。
应用程序如何安全、优雅地退出?
在Linux上很多应用通常会通过kill -9 pid的方式强制将进程杀掉,这种方式简单高效,因此很多应用的停止脚本经常会选择使用kill -9 pid的方式。但是,Server程序经常会长时间运行,在运行过程中,可能申请了很多系统资源,也可能保存了很多状态,而 kill -9 pid相当于系统宕机/系统断电,可能会带来一些副作用:
- 缓存中的数据尚未持久化到磁盘中,导致数据丢失;
- 分布式集群中数据不一致;
- 网络连接、共享内存、消息队列等得不到及时释放;
- 正在进行文件的write操作,没有更新完成,突然退出,导致文件损坏;
- 线程池的任务队列中尚有接收到的任务还没来得及处理,导致任务丢失;
- 数据库操作已经完成,例如账户余额更新,准备返回应答消息给客户端时,消息尚在通信线程的发送队列中排队等待发送,进程强制退出导致应答消息没有返回给客户端,客户端发起超时重试,会带来重复更新问题;
- 其它问题等…
一般需要在进程关闭时处理一下“善后”工作,比如
- 关闭 socket 链接
- 清理临时文件
- 发送消息通知给订阅方,告知自己下线
- 将自己将要被销毁的消息通知给子进程
- 各种资源的释放(共享内存、消息队列、互斥锁等)
可以通过捕获SIGTERM信号来实现安全、优雅地退出,如果在某次实际操作中发现:kill -15 pid 无法关闭应用,则可以考虑使用内核级别的 kill -9 pid ,但请事后务必排查出是什么原因导致 kill -15 pid 无法关闭。
实现步骤
具体来讲,通常只需要两步动作:
- 注册SIGTERM信号的处理函数并在处理函数中做一些进程退出的准备。信号处理函数的注册可以通过signal()或sigaction()来实现,其中,推荐使用后者来实现信号响应函数的设置。信号处理函数的逻辑越简单越好,通常的做法是在该函数中设置一个bool型的flag变量以表明进程收到了SIGTERM信号,准备退出。
- 在主进程的main()中,通过类似于while(!bQuit)的逻辑来检测那个flag变量,一旦bQuit在signal handler function中被置为true,则主进程退出while()循环,接下来就是一些释放资源或dump进程当前状态或记录日志的动作,完成这些后,主进程退出。
网友评论