今天在用自己在阿里云的ECS服务器(Centos)的时候试图建立一个常驻的后台程序。前面一切运行都没有问题但是在于后面关掉session(会话期)后运行的后台程序也一起关掉了(有点郁闷)。


之后开始在网上找解决方案于是就找到了两个神奇的东西及nohup和screen。
首先我们不谈原理性或者深层次的东西这两个都可以解决以上问题,使用方法:


nohup <command> [argument…] &

screen <command>

都很简单那么一开始我用了nohub来解决问题,但是接着问题又出现了,nohup虽然在我关闭session后保留了我的后台程序,但是当我想再在我的后台程序中输入指令时却发现没有办法调回前台。
于是这时候开始考虑第二解决方案screen命令。使用方法依旧很简单,但是阿里云的Centos镜像中默认是不带screen软件的,所以我们要安装:


yum install screen

简单1M不到就安装好了,然后让我们来看看工作方式:
首先Screen是一个可以在多个进程之间多路复用一个物理终端的窗口管理器。Screen中有会话的概念,用户可以在一个screen会话中创建多个screen窗口,在每一个screen窗口中就像操作一个真实的telnet/SSH连接窗口那样。在screen中创建一个新的窗口有这样几种方式:


screen
screen <command>

创建好后我们就可以运行我们的程序了,那么我们该怎么把程序放入后台呢,这时就得用到高级的(detach)screen会话功能,它可以让你的作业放到后台便于以后再调度到前台,实现方法:

在screen窗口键入C-a d(Ctrl+a再按c),Screen会给出detached提示

那么我们该怎么把程序调回前台呢:


screen -ls
screen -r <screen.id>

简单完美解决问题。

你可能注意到给screen发送命令使用了特殊的键组合C-a。这是因为我们在键盘上键入的信息是直接发送给当前screen窗口,必须用其他方式向screen窗口管理器发出命令,默认情况下,screen接收以C-a开始的命令。这种命令形式在screen中叫做键绑定(key binding),C-a叫做命令字符(command character)。


C-a ? ······显示所有键绑定信息
C-a w ······显示所有窗口列表
C-a C-a ······切换到之前显示的窗口
C-a c ······创建一个新的运行shell的窗口并切换到该窗口
C-a n ······切换到下一个窗口
C-a p ······切换到前一个窗口(与C-a n相对)
C-a 0..9······切换到窗口0..9
C-a a ······发送 C-a到当前窗口
C-a d ······暂时断开screen会话
C-a k ······杀掉当前窗口
C-a [ ······进入拷贝/回滚模式


接下来我们来看一点原理性的东西:


元凶:SIGHUP 信号
让我们来看看为什么关掉窗口/断开连接会使得正在运行的程序死掉。

在Linux/Unix中,有这样几个概念:

进程组(process group):一个或多个进程的集合,每一个进程组有唯一一个进程组ID,即进程组长进程的ID。
会话期(session):一个或多个进程组的集合,有唯一一个会话期首进程(session leader)。会话期ID为首进程的ID。
会话期可以有一个单独的控制终端(controlling terminal)。与控制终端连接的会话期首进程叫做控制进程(controlling process)。当前与终端交互的进程称为前台进程组。其余进程组称为后台进程组。
根据POSIX.1定义:

挂断信号(SIGHUP)默认的动作是终止程序。
当终端接口检测到网络连接断开,将挂断信号发送给控制进程(会话期首进程)。
如果会话期首进程终止,则该信号发送到该会话期前台进程组。
一个进程退出导致一个孤儿进程组中产生时,如果任意一个孤儿进程组进程处于STOP状态,发送SIGHUP和SIGCONT信号到该进程组中所有进程。
因此当网络断开或终端窗口关闭后,控制进程收到SIGHUP信号退出,会导致该会话期内其他进程退出。


参考资料:
“Advanced Programming in the UNIX® Environment: Second Edition” W. Richard Stevens, Stephen A. Rago 提供了更多关于Linux/Unix进程关系、信号的知识。