摘要:现在的操作系统都支持多任务,实际上是操作系统通过在一个执行中的程序和另一个程序之间快速地切换造成了一种它同时能够做多件事情的假象。Linux内核通过使用进程来管理多任务。进程,就是 Linux 组织安排正在等待使用 CPU 的各种程序的方式。

进程

在这一章中,我们将看一些可用的命令行工具,这些工具帮助我们查看程序的执行状态,以及怎样终止行为不当的进程。这一章将介绍以下命令:

  • ps - 报告当前进程快照
  • top - 显示任务
  • jobs - 列出活跃的任务
  • bg - 把一个任务放到后台执行
  • fg - 把一个任务放到前台执行
  • kill - 给一个进程发送信号
  • killall - 杀死指定名字的进程
  • shutdown - 关机或重启系统

进程是怎样工作的

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

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

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

ps - 查看进程

查看进程最常用的命令是ps。它有很多选项,简单使用形式如下:

xuxg@xuxg-ubuntu:~$ ps
  PID TTY          TIME CMD
19518 pts/0    00:00:00 bash
19544 pts/0    00:00:00 ps

上例中,列出了两个进程,进程19518和进程 19544,各自代表命令 bashps

默认情况下,ps 不会显示很多进程信息,,只是列出与当前终端会话相关的进程。

ps运行结果各个字段的含义:

PID:进程号

TTY :“Teletype”(直译电传打字机) 的简写,是指进程的控制终端。

TIME:表示进程所消耗的 CPU 时间数量。

CMD:Command的缩写,表示正在执行的命令行命令。

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

xuxg@xuxg-ubuntu:~$ ps x
  PID TTY      STAT   TIME COMMAND
 2192 ?        Ss     0:00 /lib/systemd/systemd --user
 2193 ?        S      0:00 (sd-pam)

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

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

状态 含义
R 运行中。这意味着,进程正在运行或准备运行。
S 正在睡眠。进程没有运行,而是正在等待一个事件,比如说,一个按键或者网络分组。
D 不可中断睡眠。进程正在等待 I/O,比方说,一个磁盘驱动器的 I/O。
T 已停止.。已经指示进程停止运行。稍后介绍更多。
Z 一个死进程或“僵尸”进程。这是一个已经终止的子进程,但是它的父进程还没有清空它。(父进程没有把子进程从进程表中删除)。
< 一个高优先级进程。这可能会授予一个进程更多重要的资源,给它更多的 CPU 时间。进程的这种属性叫做 niceness。具有高优先级的进程据说是不好的(less nice),因为它占用了比较多的 CPU 时间,这样就给其它进程留下很少时间。
N 低优先级进程。一个低优先级进程(一个“nice”进程)只有当其它高优先级进程被服务了之后,才会得到处理器时间。

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

另一个流行的选项组合是 aux(不带开头的 - 字符)。这会给我们更多信息:

xuxg@xuxg-ubuntu:~$ ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1 225628  9604 ?        Ss   08:33   0:05 /sbin/init spla
root         2  0.0  0.0      0     0 ?        S    08:33   0:00 [kthreadd]

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

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

top - 动态查看进程

top 程序以进程活动顺序显示连续更新的系统进程列表。(默认情况下,每三秒钟更新一次)。

top 显示结果由两部分组成:最上面是系统概要;下面是进程列表,以 CPU 的使用率排序。

xuxg@xuxg-ubuntu:~$ top

top - 14:08:14 up  5:34,  1 user,  load average: 0.27, 0.32, 0.18
Tasks: 272 total,   1 running, 215 sleeping,   0 stopped,   0 zombie
%Cpu(s): 10.1 us,  1.2 sy,  0.0 ni, 88.3 id,  0.0 wa,  0.0 hi,  0.5 si,  0.0 st
KiB Mem :  8033284 total,  4481844 free,  1643668 used,  1907772 buff/cache
KiB Swap:  9999356 total,  9999356 free,        0 used.  5994816 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND     
 2212 root      20   0  526920 123500  81780 S  28.9  1.5  12:56.84 Xorg        
 2416 xuxg      20   0 3726012 356604 106824 S   8.0  4.4   9:29.48 gnome-shell 

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

行号 字段 意义
1 top 程序名。
14:08:14 当前时间。
up 5:34 这是正常运行时间。它是计算机从上次启动到现在所运行的时间。在这个例子里,系统已经运行了5个小时34分钟。
1 user 有一个用户登录系统。
load average: 加载平均值,是指等待运行的进程数目。也就是说,处于可以运行状态并共享 CPU 的进程个数。这里展示了三个数值,每个数值对应不同的时间段。第一个是最后 60 秒的平均值,下一个是前 5 分钟的平均值,最后一个是前 15 分钟的平均值。若平均值低于 1.0,则指示计算机工作不忙碌。
2 Tasks: 总结了进程数目和这些进程的各种状态。
3 %Cpu(s): 这一行描述了 CPU 正在进行的活动的特性。
10.1 us 10.1% 的 CPU 被用于用户进程。这意味着进程在内核之外。
1.2 sy 1.2% 的 CPU 时间被用于系统(内核)进程。
0.0 ni 0.0% 的 CPU 时间被用于”nice”(低优先级)进程。
88.3 id 88.3% 的 CPU 时间是空闲的。
0.0 wa 0.0% 的 CPU 时间来等待 I/O。
4 KiB Mem 展示物理内存的使用情况。
5 KiB Swap 展示交换分区(虚拟内存)的使用情况。

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

控制进程

现在我们来学着控制进程。用xlogo作为小白鼠。

xlogo 程序是 X 窗口系统(使图形界面显示在屏幕上的底层引擎)提供的示例程序,这个程序仅显示一个大小可调的包含 X 标志的窗口。

xuxg@xuxg-ubuntu:~$ xlogo

命令执行之后,一个包含 X 标志的小窗口应该出现在屏幕的某个位置上。

中断一个进程

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

通过Ctrl+C,许多(但不是全部)命令行程序可以被中断。

把一个进程放置到后台 (执行)

假如说我们想让 shell 提示符返回,却不终止xlogo程序。我们可以把这个程序放到后台(background) 执行。

在程序命令之后加上 &字符,可以启动一个程序并让它立即在后台运行。

xuxg@xuxg-ubuntu:~$ xlogo &
[1] 26931
xuxg@xuxg-ubuntu:~$ 

执行命令之后,这个 xlogo 窗口出现,并且 shell 提示符返回,同时打印一条信息。这条信息是 shell 特性的一部分,叫做任务控制 (job control)。

通过这条信息,shell 告诉我们,已经启动了任务号 (job number) 为 1(“[1]”),PID 为 28236 的程序。如果我们运行 ps命令,可以看到我们的进程:

xuxg@xuxg-ubuntu:~$ ps
  PID TTY          TIME CMD
26905 pts/0    00:00:00 bash
26931 pts/0    00:00:00 xlogo
26932 pts/0    00:00:00 ps

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

xuxg@xuxg-ubuntu:~$ jobs
[1]+  Running                 xlogo &

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

fg - 进程返回到前台

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

xuxg@xuxg-ubuntu:~$ jobs
[1]+  Running                 xlogo &
xuxg@xuxg-ubuntu:~$ fg %1
xlogo

fg 命令之后,跟随着一个百分号和任务序号(叫做 jobspec, 如此处的%1)就可以了。如果我们只有一个后台任务,那么 jobspec(job specification) 是可有可无的。输入 Ctrl-c来终止xlogo程序。

停止一个进程

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

xuxg@xuxg-ubuntu:~$ xlogo
[1]+  Stopped                 xlogo

使用fg 命令,可以恢复程序到前台运行,或者用bg命令把程序移到后台。

Signals

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

xuxg@xuxg-ubuntu:~$ xlogo &
[1] 30334
xuxg@xuxg-ubuntu:~$ kill 30334

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

重点来了!!!

kill 命令不是真的“杀死”程序,而是给程序发送信号。

信号是操作系统与程序之间进行通信时所采用的几种方式中的一种。在使用 Ctrl-cCtrl-z 的过程中我们已经看到信号的实际用法。当终端接受了其中一个按键组合后,它会给在前端运行的程序发送一个信号。在使用 Ctrl-c的情况下,会发送一个叫做 INT(Interrupt, 中断)的信号;当使用 Ctrl-z 时,则发送一个叫做 TSTP(Terminal Stop,终端停止)的信号。程序,相应地,监听信号的到来,当程序接到信号之后,则做出响应。一个程序能够监听和响应信号这件事允许一个程序做些事情,比如,当程序接到一个终止信号时,它可以保存所做的工作。

kill - 给进程发送信号

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

kill [-signal] PID...

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

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

让我们试一下 kill 命令:

xuxg@xuxg-ubuntu:~$ xlogo &
[1] 3697
xuxg@xuxg-ubuntu:~$ kill -1 3697
xuxg@xuxg-ubuntu:~$ 
[1]+  Hangup                  xlogo

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

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

killall - 给多个进程发送信号

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

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

更多和进程相关的命令

因为监测进程是一个很重要的系统管理任务,所以有许多命令与它相关。玩玩下面几个命令:

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

总结

到这里,TLCL一书的第一部分——学习shell 就结束了。真的需要好好消化,尤其是后边权限、进程什么的,没太搞懂。