进程

有时候,计算机变得呆滞,运行缓慢,或者一个应用程序停止响应。在这一章中,我们将看一些可用的命令行工具,这些工具帮助我们查看程序的执行状态,以及怎样终止行为不当的进程。

这一章将介绍以下命令:

  • ps – 报告当前进程快照

  • top – 显示任务

  • jobs – 列出活跃的任务

  • bg – 把一个任务放到后台执行

  • fg – 把一个任务放到前台执行

  • kill – 给一个进程发送信号

  • killall – 杀死指定名字的进程

  • shutdown – 关机或重启系统

当系统启动的时候,内核先把一些它自己的活动初始化为进程,然后运行一个叫做 init 的程序。init,依次地,再运行一系列的称为 init 脚本的 shell 脚本(位于/etc),它们可以启动所有的系统服务。其中许多系统服务以守护(daemon)程序的形式实现,守护程序仅在后台运行,没有任何用户界面。这样,即使我们没有登录系统,至少系统也在忙于执行一些例行事务。

在进程方案中,一个程序可以发动另一个程序被表述为一个父进程可以产生一个子进程。

内核维护每个进程的信息,以此来保持事情有序。例如,系统分配给每个进程一个数字,这个数字叫做进程 ID 或 PID。PID 号按升序分配,init 进程的 PID 总是1。内核也对分配给每个进程的内存和就绪状态进行跟踪以便继续执行这个进程。像文件一样,进程也有所有者和用户 ID,有效用户 ID,等等。

11.1.1 使用ps命令查看进程信息

查看进程,最常使用地命令(有几个命令)是 ps。ps 程序有许多选项,它最简单地使用形式是这样的:

上例中,列出了两个进程,进程 5198 和进程 10129,各自代表命令 bash 和 ps。正如我们所看到的,默认情况下,ps 不会显示很多进程信息,只是列出与当前终端会话相关的进程。为了得到更多信息,我们需要加上一些选项,但是在这样做之前,我们先看一下 ps 命令运行结果的其它字段。TTY 是 “Teletype” 的简写,是指进程的控制终端。这里,Unix 展示它的年龄。TIME 字段表示进程所消耗的 CPU 时间数量。正如我们所看到的,这两个进程使计算机工作起来很轻松。

如果给 ps 命令加上选项,我们可以得到更多关于系统运行状态的信息:

  1. PID TTY STAT TIME COMMAND
  2. 2799 ? Ssl 0:00 /usr/libexec/bonobo-activation-server ac

加上 “x” 选项(注意没有开头的 “-“ 字符),告诉 ps 命令,展示所有进程,不管它们由什么终端(如果有的话)控制。在 TTY 一栏中出现的 “?” ,表示没有控制终端。使用这个 “x” 选项,可以看到我们所拥有的每个进程的信息。

因为系统中正运行着许多进程,所以 ps 命令的输出结果很长。为了方便查看,将ps的输出管道到less中通常很有帮助。一些选项组合也会产生很长的输出结果,所以最大化终端仿真器窗口可能也是一个好主意。

输出结果中,新添加了一栏,标题为 STAT 。STAT 是 “state” 的简写,它揭示了进程当前状态:

进程状态信息之后,可能还跟随其他的字符。这表示各种外来进程的特性。详细信息请看 ps 手册页。

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

这个选项组合,能够显示属于每个用户的进程信息。使用这个选项,可以唤醒 “BSD 风格” 的输出结果。Linux 版本的 ps 命令,可以模拟几个不同 Unix 版本中的 ps 程序的行为。通过这些选项,我们得到这些额外的列。

标题 含义
USER 用户 ID. 进程的所有者。
%CPU 以百分比表示的 CPU 使用率
%MEM 以百分比表示的内存使用率
VSZ 虚拟内存大小
RSS 进程占用的物理内存的大小,以千字节为单位。
START 进程启动的时间。若它的值超过24小时,则用天表示。

11.1.2 用 top 命令动态查看进程

虽然 ps 命令能够展示许多计算机运行状态的信息,但是它只是提供 ps 命令执行时刻的机器状态快照。为了看到更多动态的信息,我们使用 top 命令:

  1. [me@linuxbox ~]$ top

top 程序以进程活动顺序显示连续更新的系统进程列表。(默认情况下,每三秒钟更新一次),”top”这个名字来源于 top 程序是用来查看系统中“顶端”进程的。top 显示结果由两部分组成:最上面是系统概要,下面是进程列表,以 CPU 的使用率排序。

  1. top - 14:59:20 up 6:30, 2 users, load average: 0.07, 0.02, 0.00
  2. Tasks: 109 total, 1 running, 106 sleeping, 0 stopped, 2 zombie
  3. Cpu(s): 0.7%us, 1.0%sy, 0.0%ni, 98.3%id, 0.0%wa, 0.0%hi, 0.0%si
  4. Mem: 319496k total, 314860k used, 4636k free, 19392k buff
  5. Swap: 875500k total, 149128k used, 726372k free, 114676k cach
  6. PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
  7. ....

其中系统概要包含许多有用信息。下表是对系统概要的说明:

|1|top|程序名。|
||14:59:20|当前时间。|
||up 6:30|这是正常运行时间。它是计算机从上次启动到现在所运行的时间。在这个例子里,系统已经运行了六个半小时。 |
||2 users|有两个用户登录系统。|
||load average: |加载平均值是指,等待运行的进程数目,也就是说,处于可以运行状态并共享 CPU 的进程个数。这里展示了三个数值,每个数值对应不同的时间段。第一个是最后60秒的平均值,下一个是前5分钟的平均值,最后一个是前15分钟的平均值。若平均值低于1.0,则指示计算机工作不忙碌。|
|2|Tasks:|总结了进程数目和这些进程的各种状态。|
|3|Cpu(s):|这一行描述了 CPU 正在进行的活动的特性。|
||0.7%us |0.7% 的 CPU 被用于用户进程。这意味着进程在内核之外。|
||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 时间来等待 I/O。|
|4|Mem:|展示物理内存的使用情况。|
|5|Swap:|展示交换分区(虚拟内存)的使用情况。|

top 程序接受一系列从键盘输入的命令。两个最有趣的命令是 h 和 q。h,显示程序的帮助屏幕,q,退出 top 程序。

两个主要的桌面环境都提供了图形化应用程序,来显示与 top 程序相似的信息(和 Windows 中的任务管理器差别不多),但是我觉得 top 程序要好于图形化的版本,因为它运行速度快,并且消费很少的系统资源。毕竟,我们的系统监测程序不能成为我们试图追踪的系统怠工的原因。

现在我们可以看到和监测进程,让我们得到一些对它们的控制权。为了我们的实验,我们将使用一个叫做 xlogo 的小程序,作为我们的实验品。这个 xlogo 程序是 X 窗口系统(使图形界面显示在屏幕上的底层引擎)提供的示例程序,这个程序仅显示一个大小可调的包含 X 标志的窗口。首先,我们需要知道测试的实验对象:

  1. [me@linuxbox ~]$ xlogo

命令执行之后,一个包含 X 标志的小窗口应该出现在屏幕的某个位置上。在一些系统中,xlogo 命令会打印一条警告信息,但是不用理会它。


注意:

通过调整它的窗口大小,我们能够证明 xlogo 程序正在运行。如果这个标志以新的尺寸被重画,则这个程序正在运行。

注意,为什么我们的 shell 提示符还没有返回?这是因为 shell 正在等待这个程序结束,就像到目前为止我们用过的其它所有程序一样。如果我们关闭 xlogo 窗口,shell 提示符就返回了。

11.2.1 中断一个进程

我们再运行 xlogo 程序一次,观察一下发生了什么事。首先,执行 xlogo 命令,并且证实这个程序正在运行。下一步,回到终端窗口,按下 Ctrl-c。

在一个终端中,输入 Ctrl-c,中断一个程序。这意味着,我们礼貌地要求终止这个程序。输入 Ctrl-c 之后,xlogo 窗口关闭,shell 提示符返回。

通过这个技巧,许多(但不是全部)命令行程序可以被中断。

11.2.2 使进程在后台运行

假如说我们想让 shell 提示符返回,却不终止 xlogo 程序。我们可以把这个程序放到后台执行。把终端想象是一个有前台(包含在表层可见的事物,像 shell 提示符)和后台(包含表层之下的隐藏的事物)(的设备)。为了启动一个程序并让它立即在后台运行,我们在程序命令之后,加上”&”字符:

  1. [me@linuxbox ~]$ xlogo &
  2. [1] 28236
  3. [me@linuxbox ~]$

执行命令之后,这个 xlogo 窗口出现,并且 shell 提示符返回,同时打印一些有趣的数字。这条信息是 shell 特性的一部分,叫做任务控制。通过这条信息,shell 告诉我们,已经启动了任务号为1(“[1]”),PID 为28236的程序。如果我们运行 ps 命令,可以看到我们的进程:

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

shell 的任务控制功能给出了一种列出从我们终端中启动了的任务的方法。执行 jobs 命令,我们可以看到这个输出列表:

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

结果显示我们有一个任务,编号为“1”,它正在运行,并且这个任务的命令是 xlogo &。

11.2.3 使进程返回到前台

一个在后台运行的进程对一切来自键盘的输入都免疫,也不能用 Ctrl-c 来中断它。为了让一个进程返回前端,这样使用 fg 命令:

  1. [me@linuxbox ~]$ jobs
  2. [1]+ Running xlogo &
  3. [me@linuxbox ~]$ fg %1
  4. xlogo

11.2.4 停止一个进程

有时候,我们想要停止一个进程,而不是终止它。我们这么做通常是为了允许前台进程被移动到后台。输入 Ctrl-z,可以停止一个前台进程。让我们试一下。在命令提示符下,执行 xlogo 命令,然后输入 Ctrl-z:

  1. [me@linuxbox ~]$ xlogo
  2. [me@linuxbox ~]$

停止 xlogo 程序之后,通过调整 xlogo 的窗口大小,我们可以证实这个程序已经停止了。它看起来像死掉了一样。使用 fg 命令,可以恢复程序到前台运行,或者用 bg 命令把程序移到后台。

和 fg 命令一样,如果只有一个任务的话,jobspec 参数是可选的。

如果我们从命令行启动一个图形程序,但是忘了在命令后加字符 “&”, 将一个进程从前台移动到后台也是很方便的。

为什么要从命令行启动一个图形界面程序呢?有两个原因。第一个,你想要启动的程序,可能没有在窗口管理器的菜单中列出来(比方说 xlogo)。第二个,从命令行启动一个程序,你能够看到一些错误信息,如果从图形界面中运行程序的话,这些信息是不可见的。有时候,一个程序不能从图形界面菜单中启动。通过从命令行中启动它,我们可能会看到能揭示问题的错误信息。一些图形界面程序还有许多有意思并且有用的命令行选项。

kill 命令被用来“杀死”程序。这样我们就可以终止需要杀死的程序。这里有一个例子:

  1. [me@linuxbox ~]$ xlogo &
  2. [1] 28401
  3. [me@linuxbox ~]$ kill 28401
  4. [1]+ Terminated xlogo

首先,我们在后台启动 xlogo 程序。shell 打印出这个后台进程的 jobspec 和 PID。下一步,我们使用 kill 命令,并且指定我们想要终止的进程 PID。也可以用 jobspec(例如,“%1”)来代替 PID。

虽然这个命令看上去很直白, 但是它的含义不止于此。这个 kill 命令不是真的“杀死”程序,而是给程序发送信号。信号是操作系统与程序之间进行通信时所采用的几种方式中的一种。在使用 Ctrl-c 和 Ctrl-z 的过程中我们已经看到信号的实际用法。当终端接受了其中一个按键组合后,它会给在前端运行的程序发送一个信号。在使用 Ctrl-c 的情况下,会发送一个叫做 INT(中断)的信号;当使用 Ctrl-z 时,则发送一个叫做 TSTP(终端停止)的信号。程序,相应地,倾听信号的到来,当程序接到信号之后,则做出响应。一个程序能够倾听和响应信号这件事允许一个程序做些事情,比如,当程序接到一个终止信号时,它可以保存所做的工作。

11.3.1 通过 kill 命令给进程发送信号

kill 命令被用来给程序发送信号。它最常见的语法形式看起来像这样:

  1. kill [-signal] PID...

如果在命令行中没有指定信号,那么默认情况下,发送 TERM(终止)信号。kill 命令被经常用来发送以下命令:

编号 信号名字 含义
1 HUP 挂起。这是美好往昔的残留部分,那时候终端机通过电话线和调制解调器连接到远端的计算机。这个信号被用来告诉程序,控制的终端机已经“挂起”。通过关闭一个终端会话,可以展示这个信号的作用。在当前终端运行的前台程序将会收到这个信号并终止。许多守护进程也使用这个信号,来重新初始化。这意味着,当一个守护进程收到这个信号后,这个进程会重新启动,并且重新读取它的配置文件。Apache 网络服务器守护进程就是一个例子。
2 INT 中断。实现和 Ctrl-c 一样的功能,由终端发送。通常,它会终止一个程序。
9 KILL 杀死。这个信号很特别。尽管程序可能会选择不同的方式来处理发送给它的信号,其中也包含忽略信号,但是 KILL 信号从不被发送到目标程序。而是内核立即终止这个进程。当一个进程以这种方式终止的时候,它没有机会去做些“清理”工作,或者是保存工作。因为这个原因,把 KILL 信号看作最后一招,当其它终止信号失败后,再使用它。
15 TERM 终止。这是 kill 命令发送的默认信号。如果程序仍然“活着”,可以接受信号,那么这个它会终止。
18 CONT 继续。在一个停止信号后,这个信号会恢复进程的运行。
19 STOP 停止。这个信号导致进程停止运行,而不是终止。像 KILL 信号,它不被发送到目标进程,因此它不能被忽略。

让我们试一下 kill 命令:

  1. [me@linuxbox ~]$ xlogo &
  2. [1] 13546
  3. [me@linuxbox ~]$ kill -1 13546
  4. [1]+ Hangup xlogo

在这个例子里,我们在后台启动 xlogo 程序,然后通过 kill 命令,发送给它一个 HUP 信号。这个 xlogo 程序终止运行,并且 shell 指示这个后台进程已经接受了一个挂起信号。在看到这条信息之前,你可能需要多按几次 enter 键。注意,信号既可以用号码,也可以用名字来指定,包括在前面加上字母 “SIG” 的名字。

  1. [me@linuxbox ~]$ xlogo 1] 13601
  2. [me@linuxbox ~]$ kill -INT 13601
  3. [1]+ Interrupt xlogo
  4. [me@linuxbox ~]$ xlogo &
  5. [1] 13608
  6. [me@linuxbox ~]$ kill -SIGINT 13608

重复上面的例子,试着使用其它的信号。记住,你也可以用 jobspecs 来代替 PID。

进程,和文件一样,拥有所有者,所以为了能够通过 kill 命令来给进程发送信号,你必须是进程的所有者(或者是超级用户)。

除了上表列出的 kill 命令最常使用的信号之外,还有一些系统频繁使用的信号。以下是其它一些常用信号列表:

为了满足读者的好奇心,通过下面的命令可以得到一个完整的信号列表:

11.3.2 通过 killall 命令给多个进程发送信号

也有可能通过 killall 命令,给匹配特定程序或用户名的多个进程发送信号。下面是 killall 命令的语法形式:

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

为了说明情况,我们将启动一对 xlogo 程序的实例,然后再终止它们:

记住,和 kill 命令一样,你必须拥有超级用户权限才能给不属于你的进程发送信号。

命令名 命令描述
pstree 输出一个树型结构的进程列表,这个列表展示了进程间父/子关系。
vmstat 输出一个系统资源使用快照,包括内存,交换分区和磁盘 I/O。为了看到连续的显示结果,则在命令名后加上更新操作延时的时间(以秒为单位)。例如,“vmstat 5”。按下 Ctrl-c 组合键, 终止输出。
xload 一个图形界面程序,可以画出系统负载随时间变化的图形。
tload 与 xload 程序相似,但是在终端中画出图形。使用 Ctrl-c,来终止输出。