本节会介绍如何同时执行多个不同的进程并追踪它们的状态,以及如何停止或暂停某个进程,同时如何使进程在后台运行。这对于后端开发人员来说是必备技能。

目录如下:

假设我们使用sleep命令让当前的进程睡眠20秒,在这期间,通过不同的中断方式,则会终止进程的运行。如下所示:

1
2
3
4
5
carol@ubuntu-carol:~/learnShell$ sleep 20
^C
carol@ubuntu-carol:~/learnShell$ sleep 20
^\Quit (core dumped)
carol@ubuntu-carol:~/learnShell$ 

第一次使用Ctrl+c终止,第二次使用Ctrl+\终止。通过提示信息可以看到这两种不同终止方式所给出的不同结果。

这是因为:这里的 shell 会使用 UNIX 提供的信号机制进行进程之间的通信。当一个进程收到信号时,该进程会停止、处理该信号,并基于该信号传递的信息来改变其执行,即软件中断。

当使用Ctrl+c时,shell 会发送一个SIGINT信号给进程,用于中断该进程。而当使用Ctrl+\时,shell 会发送一个SIGQUIT信号给进程,用于退出进程。

下面的sigint.py程序能够捕获SIGINT信号并将其忽略,但不会让程序停止。为了停止程序,需要使用到SIGQUIT信号。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/usr/bin/env python
import signal, time

def handler(signum, time):
    print("\nI got a SIGINT, but I am not stopping")

signal.signal(signal.SIGINT, handler)
i = 0
while True:
    time.sleep(.1)
    print("\r{}".format(i), end="")
    i += 1

当执行该程序之后,我执行了两次Ctrl+c操作,显然程序还在运行中。然后,我执行了一次Ctrl+\操作,程序才停止运行。如下所示:

1
2
3
4
5
6
7
carol@ubuntu-carol:~/learnShell$ python3 sigint.py 
16^C
I got a SIGINT, but I am not stopping.
23^C
I got a SIGINT, but I am not stopping.
36^\Quit (core dumped)
carol@ubuntu-carol:~/learnShell$ 

上述演示了SIGINT信号和SIGQUIT信号的使用,除了这俩以外,SIGTERM是一个更加通用、优雅地退出信号,我们可以使用kill命令发出这个信号。

此外,使用Ctrl+z则会发送一个SIGTSTP信号,表示将进程挂起。而对于暂停或者挂起的进程,我们可以使用fgbg命令让其继续执行,前者表示在前台继续执行,后者表示在后台继续执行。

此外,jobs命令的作用是列出当前终端会话中尚未完成的全部任务。在某个命令的最后添加&这一后缀字符,可以让该命令直接在后台运行。

如果你想让正在运行的进程转到后台运行,那么可以执行Ctrl+z,然后再输入bg。不过需要注意的是:后台的进程仍然是当前终端的子进程,一旦关闭了终端,那么这些子进程也就随之终止了。为了防止这种情况的发生,可以使用nohup命令。下面是一些例子:

 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
carol@ubuntu-carol:~$ sleep 1000
^Z
[1]+  Stopped                 sleep 1000
carol@ubuntu-carol:~$ nohup sleep 2000 &
[2] 5525
carol@ubuntu-carol:~$ nohup: ignoring input and appending output to 'nohup.out'
jobs
[1]+  Stopped                 sleep 1000
[2]-  Running                 nohup sleep 2000 &
carol@ubuntu-carol:~$ bg %1
[1]+ sleep 1000 &
carol@ubuntu-carol:~$ jobs
[1]-  Running                 sleep 1000 &
[2]+  Running                 nohup sleep 2000 &
carol@ubuntu-carol:~$ kill -STOP %1
carol@ubuntu-carol:~$ jobs
[1]+  Stopped                 sleep 1000
[2]-  Running                 nohup sleep 2000 &
carol@ubuntu-carol:~$ kill -SIGHUP %1
carol@ubuntu-carol:~$ jobs
[1]-  Hangup                  sleep 1000
[2]+  Running                 nohup sleep 2000 &
carol@ubuntu-carol:~$ kill -SIGHUP %2
carol@ubuntu-carol:~$ jobs
[2]+  Running                 nohup sleep 2000 &
carol@ubuntu-carol:~$ kill %2
carol@ubuntu-carol:~$ jobs
[2]+  Terminated              nohup sleep 2000
carol@ubuntu-carol:~$ jobs
carol@ubuntu-carol:~$ 

此外,SIGKILL是一个特殊的信号,它不能被进程捕获并且它会马上结束该进程。但它的缺点是会留下孤儿进程。

接下来介绍一个终端多路复用工具 tmux,你可以在一个终端上轻松地切换多个程序,或者分离它们,此时它们会在后台运行,并将它们重新连接到另一个终端。也就是说,当你在使用终端的时候,如果想要同时执行多个任务,在运行编辑器的同时在终端的另一侧执行某个程序。你当然可以再重新打开一个终端,但是使用终端多路复用工具无疑是最好、最方便的选择。

这里有一篇关于 tmux 的文章《A Quick and Easy Guide to tmux》,介绍了 tmux 的安装及简单使用。更加详细的文章:《tmux(1) — Linux manual page》《Terminal Multiplexers》

接下来是 alias 的使用,也就是别名。例如,当你在终端输入ls -lah的时候,这个过程有些繁琐。因此,你可以使用alias ll="ls -lah"(注意:等号两边不能含有空格),然后在终端直接输入ll即可。这大幅度减少了输入命令所需要的时间。

默认情况下,shell 不会保存这些别名,你需要将这些别名配置在.bashrc文件中。像这样以.开头的文件称为dotfile,即配置文件。常见的配置文件如下所示:

  • bash:~/.bashrc、~/.bash_profile
  • git:~/.gitconfig
  • vim:~/.vimrc 和 ~/.vim 目录
  • ssh:~/ssh/config
  • tmux:~/.tmux.conf

至于这些配置文件需要怎么配置或者怎么使用,你可以在 github 上找到许多 dotfile 文件,这里 也可以找到。

对于ssh的使用,该工具可以连接到其它服务器,例如ssh foo@bar.mit.edu,即通过用户名foo登录服务器bar.mit.edu(这里的服务器也可以是 IP 地址)。

有一点没有之前没有注意到的是:可以使用ssh foobar@server ls直接在服务器server上以用户名foobar的身份执行ls的命令。甚至也可以配合管道命令来使用。

此外,当我们在使用 ssh 连接到远程服务器的时候,我们可能会遇到「软件需要监听特定设备端口」的需求。如果在本地的话,则可以直接使用localhost:端口号127.0.0.1:端口号。但是,通常情况下,服务器的远程端口不会直接通过网络暴露给用户。

我们可以通过端口转发的方式解决上述问题。转发方式有:本地端口转发和远程端口转发两种方式。如下图所示:

本地端口转发

远程端口转发

最常用的是本地端口转发,即在本地设备上某个端口建立连接并转发到远程端口上,同时远程设备上的服务监听一个端口。例如,在远程服务器上运行 Jupyter notebook 并监听8888端口,通过建立本地9999端口进行转发,即ssh -L 9999:localhost:8888 foobar@remote_server。在使用时,只需要访问本地的localhost:9999即可。

此外,对于其它的 shell 框架,比较流行的有 preztooh-my-zsh。而对于终端模拟器来说,这里展示了许多不同的终端模拟器,通过这些模拟器来对终端进行设置,除了可以配置字体、彩色主题、快捷键等功能之外,有的终端模拟器还支持 GPU 加速,例如 Alacrittykitty