第10章:进程

现代操作系统通常是多任务(multitasking)的,意味着它们通过迅速地从一个正在执行的程序切换到另一个程序,来创造一种计算机在同一时间内能做多个事务的错觉。Linux 内核用进程(processes)管理多任务。进程是 Linux 如何组织各个不同程序以等待 CPU 处理它们的程序。

有时,计算机会变得很迟钝,或者一个应用程序会停止响应。在本章中,我们将看到命令行中一些可用的工具,让我们检查程序正在做什么,并终结那些错误行为的进程。

本章将介绍下列命令:

  • ps 报告当前所有进程的快照

  • top 显示任务

  • jobs 列出活动的工作

  • bg 将一个工作置于后台

  • fg 将一个工作置于前台

  • kill 发送信号给进程

  • killall 通过名称杀掉进程

  • shutdown 关闭或重启系统

一个进程如何工作

当系统启动,内核发起一些自己的活动作为进程,并启动一个名为 init 的程序。init 反过来运行一系列被称为初始脚本(init scripts)的 shell 脚本(位于 /etc)以启动所有的系统服务。许多服务被当作守护程序(daemon programs)来执行,即程序仅仅在后台呆着,做着自己的工作,没有任何用户界面。所以,即使我们没有登录,系统已经在忙于开展一些例行事务了。

一个程序能运行其它程序,在进程方案中被表述为一个父进程(parent process)制造了一个子进程(child process)。

内核维护着关于每个进程的信息,以帮助保持良好的组织性。例如,每个进程被分配到一个称为进程 ID(process IDPID)的数字。PID 按升序分配,init 进程总是得到 1 号 PID。内核还会跟踪分配给每个进程的内存,以及进程恢复执行的准备情况。和文件一样,进程也拥有属主和用户 ID、有效用户 ID 等。

查看进程

查看进程的几个命令中,最常用的一个是 psps 程序有很多选项,但是最近单的使用形式是这样的:

[me@linuxbox ~]$ ps
  PID    TTY          TIME    CMD
 5198    pts/1    00:00:00    bash
10129    pts/1    00:00:00    ps

上例结果中列出了两个进程,分别是 5198 号的 bash10129 号的 ps。我们可以看到,默认情况下 ps 不会显示给我们很多,而仅仅显示与当前终端会话关联的进程。要查看更多进程,我们需要加一些选项,不过首先让我们看一下 ps 输出的其它各个字段。TTY 是 "teletype" 的简写,指进程的控制终端(controlling terminal)。Unix 在这里显示该进程的寿命。TIME 字段是该进程消耗 CPU 的时长。如我们所见,没有进程让计算机工作得很辛苦。

如果我们加个选项,我们就能得到一张系统正在干嘛的更大的图片。

[me@linuxbox ~]$ ps x
  PID TTY STAT TIME  COMMAND
 2799 ?   Ssl  0:00  /usr/libexec/bonobo-activation-server –ac
 2820 ?   Sl   0:01  /usr/libexec/evolution-data-server-1.10 --
15647 ?   Ss   0:00  /bin/sh /usr/bin/startkde
15751 ?   Ss   0:00  /usr/bin/ssh-agent /usr/bin/dbus-launch --
15754 ?   S    0:00  /usr/bin/dbus-launch --exit-with-session
15755 ?   Ss   0:01  /bin/dbus-daemon --fork --print-pid 4 –pr
15774 ?   Ss   0:02  /usr/bin/gpg-agent -s –daemon
15793 ?   S    0:00  start_kdeinit --new-startup +kcminit_start
15794 ?   Ss   0:00  kdeinit Running...
15797 ?   S    0:00  dcopserver –nosid
and many more...

加了 "x" 选项(注意,没有前置的连字符)告诉 ps 显示所有进程,不论这些进程由哪个终端控制。TTY 字段的 ? 指示没有控制终端。使用这个选项,我们看到了属于自己的全部进程。

由于系统正在运行许多进程,ps 也产生了一个很长的清单。所以用将 ps 的输出管道输入到 less 会更便于查看。一些选项合并也会产生很多行的输出,所以最大化终端模拟器窗口,也是个好主意吧。

有个新字段 STAT 出现在输出中。STAT 是 "state" 的简写,揭示进程的当前状态,如表 10-1 所示。

表 10-1:进程状态

状态

意义

R

运行中。意为进程正在运行或准备运行。

S

睡眠中。进程没有在运行;在等待一个事件,如一次击键,或一个网络包。

D

不间断睡眠。进程正在等待输入输出,如磁盘驱动器。

T

停止中。该程序已被指示停止。本章稍后详述。

Z

已失效或「僵尸」进程。已经终止到没有被其父进程清理的子进程。

<

高优先级进程。可能是授予一个进程更高的重要性,给予其更多的 CPU 时间。这个进程的属性被称为nice 值(niceness)。一个高优先级的进程被唤作具有低 nice 值,因为它拿走了更多的 CPU 时间,留给其它进程的就更少了。

N

低优先级进程。具有低优先级的进程(一个「好」进程)仅在其它高优先级进程得到服务之后才得到处理器时间。

进程状态可能还会跟随其它字符。这指示进程具有特殊的性质。

另一个广泛使用的选项组合是 aux(没有前置连字符)。这给予我们更多信息。

[me@linuxbox ~]$ ps aux
USER PID %CPU %MEM  VSZ RSS TTY STAT START TIME COMMAND
root   1  0.0  0.0 2136 644 ?   Ss   Mar05 0:31 init
root   2  0.0  0.0    0   0 ?   S<   Mar05 0:00 [kt]
root   3  0.0  0.0    0   0 ?   S<   Mar05 0:00 [mi]
root   4  0.0  0.0    0   0 ?   S<   Mar05 0:00 [ks]
root   5  0.0  0.0    0   0 ?   S<   Mar05 0:06 [wa]
root   6  0.0  0.0    0   0 ?   S<   Mar05 0:36 [ev]
root   7  0.0  0.0    0   0 ?   S<   Mar05 0:00 [kh]
and many more...

这个选项组合显示了所有用户的进程。使用不带前导短划线的选项会调用具有「BSD 样式」行为的命令。Linux 版本的 ps 可以模拟多个不同的 Unix 实现中的 ps 程序。使用这些选项,我们能获得额外的字段,如表 10-2 所示。

表 10-2:BSD 风格的 ps 行首

头部

意义

USER

用户 ID。该进程的属主。

%CPU

CPU 使用率的百分比。

%MEM

内存使用率的百分比。

VSZ

虚拟内存大小。

RSS

驻留集大小。此为该进程物理内存(RAM)的用量,用 KB 为单位。

START

进程开始的时间。若超过 24 小时,则使用日期表示。

用 top 动态查看进程

ps 命令可以查看机器在做什么,它仅仅提供机器在执行 ps 命令的那一刻状态的快照。要查看机器的动态活动,就需要使用 top 命令:

[me@linuxbox ~]$ top

top 程序显示持续更新(默认每三秒刷新一次)的系统进程列表,以进程活跃程度排序。这个程序的得名 top,就因为该程序常用来查看系统中置顶的进程。top 显示分为两部分:首先显示的是系统摘要信息,紧跟着以 CPU 活动为序列出进程详单。

top output

系统摘要包含了一大堆好东西。这里会给出解释:

表 10-3:top 信息字段

字段

意义

1

top

程序名称。

14:59:20

当前时间。

up 6:30

运行时间(uptime)。指距离上一次启动机器的时长。在这个例子中,系统已经运行了六小时三十分钟。

2 users

当前有两个用户登录着。

load average

平均负载(Load average)指等待运行的进程数,即正在共享 CPU 的处在可运行状态的进程数。显示三个数值,每个指不同的时间段:第一个是 60 秒内的,第二个是 5 分钟内的,最后一个是过去 15 分钟内的。数值小于 1.0 意味着机器不忙碌。

2

Tasks:

各种状态的进程数小计。

3

Cpu(s)

此行描述 CPU 正在执行的活动的特征。

0.7%us

0.7% 的 CPU 用在用户进程(user processes)。意味着内核外的进程。

1.0%sy

1.0% 的 CPU 用在系统(内核)进程。

0.0%ni

0.0% 的 CPU 用在 "nice"(低优先级)的进程。

98.3%id

98.3% 的 CPU 空闲。

0.0%wa

0.0% 的 CPU 正在等待输入输出。

4

Mem:

显示物理内存的使用状况。

5

Swap:

显示交换空间(虚拟内存)的使用状况。

top 程序接受一些键盘命令。两个最有趣的分别是 h 显示程序帮助信息,q 退出 top

两个主要的桌面环境都提供图形应用程序显示类似于 top 的信息(类似在 Windows 中的任务管理器),但是 top 优于图形版本,因为它更快,且消耗更少的系统资源。毕竟,我们的系统监控程序不应该是我们正试图跟踪的引起系统变慢的来源。

控制进程

既然我们可以查看监控进程,我们也可以控制它们。对于我们的实验,将使用一个名叫 xlogo 的小程序来当我们的小白鼠。xlogo 程序是一个由 X 窗口系统(使图形得以在显示器上运行的底层引擎)提供的样本程序,很简单地显示一个可重置尺寸的包含 X 标识的窗体。首先,我们将了解一下测试项目。

[me@linuxbox ~]$ xlogo

键入命令之后,一个包含标识的窗体显示在屏幕上。有些系统上,X 标识上会印上一个警告信息,但不会有危险,可以忽略。

技巧:如果你的系统没有包含 xlogo 程序,试着用 geditkwrite 替代。

我们可以通过变化其窗口尺寸来验证 xlogo 正在运行。如果标识随窗口大小而变化,即表明程序在运行。

注意 shell 怎么会没有返回提示符?这是因为 shell 正在等待程序结束,和我们已经用过的其它程序类似。如果我们关闭了 xlogo 窗体,就会返回提示符了。

xlogo

中断一个进程

让我们再次观察当 xlogo 运行时发生了什么。首先,键入 xlogo 命令并验证程序正在运行。然后,回到终端窗口键入 Ctrl-c

[me@linuxbox ~]$ xlogo
[me@linuxbox ~]$

在终端中键入 Ctrl-c,会中断(interrupts)一个程序。这意味着我们礼貌的要求程序终结。当我们按下 Ctrl-c 之后,xlogo 窗体被关闭,shell 提示符也得以返回。

很多(但不是全部)的命令行程序可以用这种技术中断。

将一个进程置于后台

现在我们想要返回提示符但又不终结 xlogo 程序。可以通过将程序置于后台(background)来实现。将终端想象成具有一个前台(foreground 在表面有可见的事物,如提示符)和一个后台(藏在表面之下的事物)。要启动一个程序且使其立即转入后台,我们需要在命令之后跟随一个与号(&)。

[me@linuxbox ~]$ xlogo &
[1] 28236
[me@linuxbox ~]$

键入命令之后,xlogo 窗体出现,而且提示符也返回了,但是有些好玩的数字也被打印在屏幕上。这个条信息是名为工作控制(job control)的 shell 特性的一部分。在这条信息中,shell 告诉我们已经开启了编号为 [1] 的工作,其进程 ID 为28236。如果我们运行 ps,可以看到该进程。

[me@linuxbox ~]$ ps
PID   TTY       TIME CMD
10603 pts/1 00:00:00 bash
28236 pts/1 00:00:00 xlogo
28239 pts/1 00:00:00 ps

Shell 的工作控制功能还给予了我们列出从终端启动的工作列表的方法。使用 jobs 命令,我们能看到这个列表:

[me@linuxbox ~]$ jobs
[1]+ Running                xlogo &

结果显示我们有一个编号为 [1] 的工作,正在运行,其命令是 xlogo &

将一个进程带回前台

一个后台进程是对终端键盘输入免疫的,包括用 Ctrl-c 中断该进程的企图也不能奏效。要将进程带回前台,需要这样使用 fg 命令。

[me@linuxbox ~]$ jobs
[1]+ Running                xlogo &
[me@linuxbox ~]$ fg %1
xlogo

技巧是 fg 命令跟随着一个百分号和工作编号(jobspec)。如果我们仅有一个后台工作,工作编号可以省略。要终结 xlogo,请按 Ctrl-c

终止(暂停)一个进程

有时我们会想要在不终止(stop)进程的情况下停止(terminate)进程。通常这样做是为了将前台进程移动到后台。要中断一个前台进程并将其移入后台,按 Ctrl-z。来试一下。在命令提示符处,键入 xlogo,按 Enter 键,然后按 Ctrl-z

[me@linuxbox ~]$ xlogo
[1]+ Stopped                xlogo
[me@linuxbox ~]$

在中断 xlogo 之后,我们可以通过尝试调整 xlogo 窗体大小来验证该程序该程序已经中断。我们会看到它表现得跟死了一样。我们可以用 fg 命令让程序在前台继续执行,或用 bg 命令在后台恢复程序的执行:

[me@linuxbox ~]$ bg %1
[1]+ xlogo &
[me@linuxbox ~]$

fg 命令,若仅有一个工作,工作编号是可有可无的。

如果我们从命令行启动图形程序,但是忘记通过附加尾部 将它置于后台,将其从前台移动到后台还是很方便的。

为何要从命令行启动一个图形程序?有两个理由:

  • 我们要运行的程序可能在窗口管理器中找不到(如 xlogo)。

  • 通过命令行启动程序,我们可以看到一些在图形界面启动时看不到的错误信息。有时,一个程序从图形界面启动失败,通过从命令行启动,我们能看到一条错误信息以揭示问题。还有,一些图形程序有一些有用的命令行选项。

信号

kill 命令用来「杀死」进程。它允许我们终结需要杀死的程序(即一些暂停或终结的程序)。这里有个例子:

[me@linuxbox ~]$ xlogo &
[1] 28401
[me@linuxbox ~]$ kill 28401
[1]+ Terminated                xlogo

首先,我们在后台启动 xlogo。Shell 打印出工作编号和后台的进程 ID。然后我们用 kill 命令并指定想要终结的进程 ID。也可以用工作编号(如 %1)替代进程 ID。

这看起来非常简洁明了,不过其实并非如此。实际上 kill 命令并没有「杀死」进程:而是给它们发送了信号。信号是操作系统与程序通信的几种途径之一。我们已经看到 Ctrl-cCtrl-z 的行为。当终端接收到这些击键后,会发送一个信号到前台程序。如果是 Ctrl-c,一个称为 INT(interrupt)的信号被发送;Ctrl-z 则触发 TSTP(terminal stop)。程序,反过来,「监听」信号,当它们收到信号后,会根据信号有所行动。程序可以监听并对信号进行操作这一事实允许程序执行诸如在发送终止信号时保存正在进行的工作等事情。

用 kill 发送信号给进程

kill 命令用来给程序发送信号。最常用的句法如下:

kill [-signal] PID...

如果在命令行中没有指定信号,默认会发送 TERMterminate 终止)。kill 命令最常用的信号有:

表 10-4:常用信号

编号

名称

意义

1

HUP

挂起(Hangup)。这是很久前留下的痕迹了,那时,远程计算机接入终端还需要通过电话线和调制解调器。这个信号用来指示程序那正在控制的终端已经「挂起」。这个信号的作用可以通过关闭终端会话来演示。运行在终端前台的程序会被发送该信号,且终止运行。 该信号也被许多守护程序使用,以引发重新初始化。这意味着当一个守护进程被发送了该信号,它将重启动并重新读取其配置文件。Apache 网络服务器就是以这种方式使用 HUP 信号的。

2

INT

中断(Interrupt)。与按下 Ctrl-c 的功效相同。通常会终止一个程序。

9

KILL

杀(Kill)。这个信号很特殊。鉴于程序能选择处理通过各种途径发送到它们的信号,也包括忽略它们,KILL 信号实际上不会被发送到目标程序。然而,内核立即终结了进程。当进程以这种方式被终结,它将没有机会「清理」自身或保存其工作。 由此,KILL 信号应该仅用来作为在其它信号失效时的最后一招。

15

TERM

终止(Terminate)。这是 kill 命令默认发送的信号。如果一个程序仍然「活着」且能接收信号,它将终止。

18

CONT

继续(Continue)。在 STOPTSTP 信号之后,该信号会恢复一个进程。该信号有 bgfg 命令发送。

19

STOP

停止(Stop)。该信号会导致一个进程暂停,不会终结。和 KILL 信号一样,它不会被发送到目标进程,所以也不会被忽略。

20

TSTP

终结(Terminal stop)。当按下 Ctrl-z 时,该信号被发送。与 STOP 不同,该信号由程序接收,但程序可以选择忽略。

来试验一下 kill 命令:

[me@linuxbox ~]$ xlogo &
[1] 13546
[me@linuxbox ~]$ kill -1 13546
[1]+ Hangup                   xlogo

在该例中,我们在后台启动了 xlogo 程序,然后用 killHUP 信号发送给它。xlogo 程序终结,且 shell 指示后台进程已经收到一个挂起信号。在消息出现前,我们可能需要按住 Enter 键一会儿。注意,信号可以通过编号或名称来指定, 包括在名称前加前缀 SIG

[me@linuxbox ~]$ xlogo &
[1] 13601
[me@linuxbox ~]$ kill -INT 13601
[1]+ Interrupt                xlogo
[me@linuxbox ~]$ xlogo &
[1] 13608
[me@linuxbox ~]$ kill -SIGINT 13608
[1]+ Interrupt                xlogo

重复上面的例子,并尝试其它信号。记住,我们还可以用工作编号替代进程编号。

进程,如同文件,是有属主的,必须是进程的属主(或超级用户),才能用 kill 发送信号到进程。

在上表所列信号之外,系统最常用的 kill 信号,列在表 10-5。

表 10-5 其它常用信号

编号

名称

意义

3

QUIT

退出(Quit)。

11

SEGV

分段违规(Segmentation violation)。 如果程序非法使用内存,就会发送此信号,即,如果它试图在不被允许的地方写入。

28

WINCH

改变窗口(Window change)。 这是当窗口改变大小时系统发送的信号。 某些程序(如 topless)将通过重新绘制自身以适应新的窗口尺寸来响应此信号。

要满足好奇心,下列命令将显示一份完整的信号列表:

[me@linuxbox ~]$ kill -l

用 killall 发送信号给多个进程

还能用 killall 命令发送信号到符合一个指定程序或用户名的多个进程。句法如下:

killall [-u user] [-signal] name...

为了演示,我们将启动一些 xlogo 程序的示例,然后终止它们。

[me@linuxbox ~]$ xlogo &
[1] 18801
[me@linuxbox ~]$ xlogo &
[2] 18802
[me@linuxbox ~]$ killall xlogo
[1]- Terminated               xlogo
[2]+ Terminated               xlogo

记住,和用 kill 一样,我们必须具有超级用户权限来发送信号给那些不属于我们的进程。

关闭系统

关闭系统的进程,涉及到在系统关闭前按序终结系统中的所有进程,如同执行一些重要的家庭琐事(如同步所有已加载的文件系统)。有四个命令能执行此功能,haltpoweroffrebootshutdown。前三个顾名思义,使用时通常也不带任何参数。例:

[me@linuxbox ~]$ sudo reboot

shutdown 命令更有趣一点。通过它,我们能指定执行何种操作(中止,关闭电源,重启动)并可以给关闭系统事件提供一个事件延迟。最常用的是像这样关闭系统:

[me@linuxbox ~]$ sudo shutdown -h now

或者像这样重启系统:

[me@linuxbox ~]$ sudo shutdown -r now

延迟可以通过各种途径来指定。查看 shutdown 的手册页以获取更多细节。一旦 shutdown 命令得以执行,一条信息将「广播」到所有已登录用户,来警告他们即将发生的事件。

由于监控进程是很重要的系统管理任务,系统还提供了很多命令。表 10-6 列举了一些:

表 10-6:其它进程相关命令

命令

描述

pstree

输出以树状结构排列的进程列表,以显示进程间的亲子关系。

vmstat

输出系统资源的使用情况的快照,包含内存、交换空间和磁盘输入输出。要查看持续的显示,在命令之后跟随一个时间延迟(以秒计)以刷新。例如:vmstat 5。按 Ctrl-c 终止输出。

xload

一个能用图形显示随时间变化的系统负载的程序。

tload

类似 xload 程序,不过是在终端中显示图形,按 Ctrl-c 终止。

总结

多数现代操作系统都提供管理多进程的机制。Linux 为该功能提供了相当丰富的工具集。鉴于 Linux 是世界上部署最多的服务器操作系统,这很有意义。然而,不同于一些别的系统,Linux 主要依赖命令行工具来管理进程。尽管 Linux 有一些图形进程工具,命令行工具因为其速度和轻量而更受青睐。图形用户界面的工具,其漂亮的外观本身也会造成很多系统负载,这在一定程度上违背了目的。

Last updated

Was this helpful?