使用Screen管理Linux远程会话

通过 SSH 或者 telent 远程登录到 Linux服务器执行一些长时间运行的任务,比如系统备份、ftp 传输等等。因为他们执行的时间太长了。必须等待它执行完毕,在此期间可不能关掉窗口或者断开连接,否则这个任务就会被杀掉,一切半途而废了。 本文分析了这个问题的原因以及解决方法。

为什么关掉窗口/断开连接会使得正在运行的程序死掉?

元凶:SIGHUP 信号 在Linux/Unix中,有这样几个概念:

  • 进程组(process group):一个或多个进程的集合,每一个进程组有唯一一个进程组ID,即组长进程的ID

  • 会话(session):一个或多个进程组的集合,开始于用户登录,终止与用户退出,此期间所有进程都属于这个会话。一个会话一般包含一个会话首进程、一个前台进程组和一个后台进程组

  • 守护进程(daemon):Linux大多数服务都是通过守护进程实现的,完成许多系统任务如0号进程为调度进程,是内核一部分;1号进程为init进程,负责内核启动后启动Linux系统。守护进程不因为用户、终端或者其他的变化而受到影响。

终端接口检测到网络连接断开,将挂断信号(SIGHUP)发送给控制进程(会话期首进程)。而挂断信号默认的动作是终止程序。如果会话期首进程终止,则该信号发送到该会话期前台进程组

也就是说:ssh打开以后,bash等都是他的子程序,一旦ssh关闭,系统将所有前台进程杀掉。(后台进程和守护进程不会被关闭!!!)    ## 测试案例 ### 测试例一 打开两个SSH终端窗口,在其中一个运行了一个循环打印的python脚本。执行命令如下: [root@localhost ~]# python test.py test.py内容如下:

1
2
while True:
print "hehe"
另外一个终端用pstree -p查看当前的进程树。显示如下
1
2
3
4
[root@localhost ~]# pstree -p
(省略)
├─sshd(958)─┬─sshd(1282)───bash(1286)───pstree(1436)
│ └─sshd(1410)───bash(1414)───python(1433)
可以看到2个bash进程代表了2个终端,pstree是当前进程正在运行的程序,而python进程则是另外一个终端正在运行的程序。

关掉启动python的终端,在刚刚执行pstree的终端上查找pid为1433的进程(也就是原来的python进程),发现没有这个pid的进程,说明python随着终端的关闭而终止了,此时输入pstree -p变为了下面这样:

1
2
3
4
[root@localhost ~]# pstree -p
(省略)
├─sshd(958)──sshd(1282)───bash(1286)───pstree(1436)

测试例二

步骤同例一,只是在执行python脚本时将其放到后台执行,执行命令如下: [root@localhost ~]# python test.py & 这样在关闭执行python的中断后,python进程并没有被中断,通过pstree -p查看到进程数类似于下面的情况:

1
2
3
4
(省略)
├─python(1493)
├─sshd(958)───sshd(1282)───bash(1286)───pstree(1497)

因为python执行的是个后台进程,而SIGHup信号只会发送给前台进程组,当父进程结束后,其原来子进程中的后台进程会成为孤儿进程被init进程收养。详见孤儿进程和僵尸进程

注:网上一些资料显示执行某些复杂程序的时候,只加&也会终止,但是博主还没遇到过这种情况,因为我不会这样去执行一个执行时间较长的程序。

同样,nohup命令可以达到这个目的,值得注意的是nohup命令只是使得程序忽略SIGHUP信号,还需要使用标记&把它放在后台运行。这种情况能够保证程序不会被终止。 nohup <command> [argument…] &

使用screen管理远程会话

虽然nohup和后台进程很容易使用,但还是比较“简陋”的,对于简单的命令能够应付过来,对于复杂的需要人机交互的任务就麻烦了。

其实我们可以使用一个更为强大的实用程序screen。

简单来说,Screen是一个可以在多个进程之间多路复用一个物理终端的窗口管理器。Screen中有会话的概念,用户可以在一个screen会话中创建多个screen窗口。

创建新的会话

在screen中创建一个新的会话有2种方式

1.直接在命令行键入screen命令 [root@localhost ~]# screen Screen将创建一个执行shell的全屏窗口。你可以执行任意shell程序,就像在ssh窗口中那样。在该窗口中键入exit退出该窗口,如果这是该screen会话的唯一窗口,该screen会话退出,否则screen自动切换到前一个窗口。 也可通过screen -S name 来为启动的session取名字。 2.Screen命令后跟你要执行的程序 [root@localhost ~]# python test.py Screen创建一个执行python test.py的单窗口会话,终止进程将退出该窗口/会话。

进入已创建会话

即使关闭了启动所有终端,在screen会话中启动的进程也不会终止,再次连接时可通过screen -ls查看已经启动的screen会话(detached状态),用screen -r name恢复指定会话,也可在会话中通过exit退出screen会话。

1
2
3
4
5
6
7
8
[root@localhost ~]# screen -ls
There is a screen on:
1518.lc (Detached)
1 Socket in /var/run/screen/S-root.
重新连接会话
[root@localhost ~]# screen -r lc或screen -r 1518
退出当前screen会话
[root@localhost ~]#exit

screen的一些常用参数如下所示

  • 分享操作

screen -x name进入一个还在连接着(attached)的screen,然后所有操作能够被另外所有正在连着的screen看到

  • 分屏
  • 创建一个新的窗口:ctrl+a+S (注意是大写的s),此时新的窗口还没启动bash
  • 启动新窗口的bash:ctrl+a+c
  • 切换窗口:ctrl+a+tab
  • 关掉当前窗口:ctrl+a+X(注意是大写的x)