linux 技巧:使用 screen 管理你的远程会话

alt

有本事,你来顺着网线爬过来,你来咬我呀

前言

你是不是经常需要 SSH 或者 telent 远程登录到 Linux 服务器?你是不是经常为一些长时间运行的任务而头疼,比如系统备份、ftp 传输等等。通常情况下我们都是为每一个这样的任务开一个远程终端窗口,因为他们执行的时间太长了。必须等待它执行完毕,在此期间可不能关掉窗口或者断开连接,否则这个任务就会被杀掉,一切半途而废了。

元凶:SIGHUP 信号

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

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

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

根据POSIX.1定义:

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

因此当网络断开或终端窗口关闭后,控制进程收到SIGHUP信号退出,会导致该会话期内其他进程退出。

我们来看一个例子。打开两个SSH终端窗口,在其中一个运行top命令。

1
test_dbs2 ~ # top

在另一个终端窗口,找到top的进程ID为15371,其父进程ID为10034,即登录shell。

1
test_dbs2 ~ # ps -ef|grep top

1
2
3
4
5
root         5     2  0 Apr01 ?        00:00:00 [stopper/0]
root 8 2 0 Apr01 ? 00:00:00 [stopper/1]
root 15371 10034 0 18:37 pts/1 00:00:00 top
root 15380 15352 0 18:37 pts/2 00:00:00 grep --colour=auto top
test_dbs2 ~ #

使用pstree命令可以更清楚地看到这个关系:

1
2
3
test_dbs2 ~ # pstree -H 15371|grep top    
| |-sshd---sshd---bash---sudo---bash---sudo---bash---top
test_dbs2 ~ #

使用ps-xj命令可以看到,登录shell(PID 15371)和top在同一个会话期,shell为会话期首进程,所在进程组PGID为15371,top所在进程组PGID为9410,为前台进程组。

1
2
3
4
5
6
7
8
test_dbs2 ~ # ps -xj|grep 15371
Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ
9410 10020 10020 9410 pts/1 15371 S 0 0:00 sudo bash
10020 10021 10021 9410 pts/1 15371 S 0 0:00 bash
10021 10033 10033 9410 pts/1 15371 S 0 0:00 sudo bash
10033 10034 10034 9410 pts/1 15371 S 0 0:00 bash
10034 15371 15371 9410 pts/1 15371 S+ 0 0:00 top
15352 15485 15484 15327 pts/2 15484 R+ 0 0:00 grep --colour=auto 15371

关闭第一个SSH窗口,在另一个窗口中可以看到top也被杀掉了。

1
2
3
test_dbs2 ~ # ps -ef|grep 15371
root 15511 15352 0 18:42 pts/2 00:00:00 grep --colour=auto 15371
test_dbs2 ~ #

如果我们可以忽略SIGHUP信号,关掉窗口应该就不会影响程序的运行了。nohup命令可以达到这个目的,如果程序的标准输出/标准错误是终端,nohup默认将其重定向到nohup.out文件。值得注意的是nohup命令只是使得程序忽略SIGHUP信号,还需要使用标记&把它放在后台运行。

1
nohup <command> [argument…] &

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

其实我们可以使用一个更为强大的实用程序screen。流行的Linux发行版(例如Red Hat Enterprise Linux )

通常会自带screen实用程序,如果没有的话,可以从GNU screen的官方网站下载。

开始使用Screen

简单来说,Screen是一个可以在多个进程之间多路复用一个物理终端的窗口管理器。Screen中有会话的概念,用户可以在一个screen会话中创建多个screen窗口,在每一个screen窗口中就像操作一个真实的telnet/SSH连接窗口那样。在screen中创建一个新的窗口有这样几种方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
test_dbs2 ~ # yum install -y screen
test_dbs2 ~ #
test_dbs2 ~ # screen --help
Use: screen [-opts] [cmd [args]]
or: screen -r [host.tty]

Options:
-4 Use IPv4.
-6 Use IPv6.
-a Force all capabilities into each window's termcap.
-A -[r|R] Adapt all windows to the new display width & height.
-c file Read configuration file instead of '.screenrc'.
-d (-r) Detach the elsewhere running screen (and reattach here).
-dmS name Start as daemon: Screen session in detached mode.
-D (-r) Detach and logout remote (and reattach here).
-D -RR Do whatever is needed to get a screen session.
-e xy Change command characters.
-f Flow control on, -fn = off, -fa = auto.
-h lines Set the size of the scrollback history buffer.
-i Interrupt output sooner when flow control is on.
-l Login mode on (update /var/run/utmp), -ln = off.
-list or -ls. Do nothing, just list our SockDir.
-L Turn on output logging.
-m ignore $STY variable, do create a new screen session.
-O Choose optimal output rather than exact vt100 emulation.
-p window Preselect the named window if it exists.
-q Quiet startup. Exits with non-zero return code if unsuccessful.
-r Reattach to a detached screen process.
-R Reattach if possible, otherwise start a new session.
-s shell Shell to execute rather than $SHELL.
-S sockname Name this session .sockname instead of ...
-t title Set title. (window's name).
-T term Use term as $TERM for windows, rather than "screen".
-U Tell screen to use UTF-8 encoding.
-v Print "Screen version 4.00.03 (FAU) 23-Oct-06".
-wipe Do nothing, just clean up SockDir.
-x Attach to a not detached screen. (Multi display mode).
-X Execute as a screen command in the specified session.

直接在命令行键入screen命令

1
test_dbs2 ~ # screen

或者添加一个名称为mysqldump

1
test_dbs2 ~ # screen -S mysqldump

之后我们想暂时退出做点别的事情,比如出去散散步,那么在screen窗口键入C-a d,Screen会给出detached提示:

暂时中断会话

半个小时之后回来了,找到该screen会话:

1
test_dbs2 ~ # screen -ls

重新连接会话:

1
2
3
test_dbs2 ~ # screen -r -S mysqldump
#或者
test_dbs2 ~ # screen -r 16582

看看出现什么了,太棒了,一切都在。继续干吧。

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

可以通过C-a ?来查看所有的键绑定,常用的键绑定有:

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 [ 进入拷贝/回滚模式

使用键绑定C-a ?命令可以看到, 默认的命令字符(Command key)为C-a,转义C-a(literal ^a)的字符为a:

如果由于某种原因其中一个会话死掉了(例如人为杀掉该会话),这时screen -list会显示该会话为dead状态。使用screen -wipe命令清除该会话:

1
2
3
test_dbs2 ~ # kill -9 8462
test_dbs2 ~ # screen -wipe
test_dbs2 ~ # screen -ls

-------------本文结束感谢您的阅读-------------

本文标题:linux 技巧:使用 screen 管理你的远程会话

文章作者:Wang Jiemin

发布时间:2019年04月02日 - 18:04

最后更新:2019年04月02日 - 19:04

原始链接:https://jiemin.wang/2019/04/02/linux-screen/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%