监测程序
一、探查进程
当程序运行在系统上时,我们称之为进程(process)。想要监测这些进程,需要熟悉ps命令的用法。ps命令好比工具中的瑞士军刀,它能输出运行在系统上的所有程序的许多信息。
但是随着ps命令的稳健而来的还有复杂性--有数不清的参数,这或许让ps命令成了最难掌握的命令。大多数系统管理员在掌握了能提供他们需要信息的一组参数后,就一直坚持只使用这组参数。
默认情况下,ps命令并不会提供那么多的信息:
1 xiaoyu@ubuntu:~$ ps
2 PID TTY TIME CMD
3 2051 pts/18 00:00:00 bash
4 2162 pts/18 00:00:00 ps
5 xiaoyu@ubuntu:~$
默认情况下,ps命令只会显示运行在当前控制台下的属于当前用户的进程,在此例中,我们只运行了bash shell(shell也只是运行在系统上的另一个程序而已)以及ps命令本身。
上例中的基本输出显示了程序的进程ID(Process ID, PID)、它们运行在哪个终端(TTY)以及进程已用的CPU时间。
Linux系统中使用的GNU ps命令支持3种不同类型的命令行参数:
- Unix风格的参数,前面加单破折线;
- BSD风格的参数,前面不加破折线;
- GNU风格的长参数,前面加双破折线。
1、Unix风格的参数
Unix风格的参数是从贝尔实验室开发的AT&T Unix系统上原有的ps命令继承下来的,这些参数如下表所示
参数 | m描述 |
-A
|
显示所有进程
|
-N
|
显示与指定参数不符的所有进程 |
-a | 显示除控制进程(session leader)和无终端进程外的所有进程 |
-d | 显示除控制进程外的所有进程 |
-e | 显示所有进程 |
-C cmdlist | 显示包含在cmdlist列表中的进程 |
-G grplist | 显示组ID在grplist列表中的进程 |
-U userlist | 显示属主的用户ID在userlist列表中的进程 |
-g grplist | 显示会话或组ID在grplist列表中的进程 |
-p pidlist | 显示PID在pidlist列表中的进程 |
-s sesslist | 显示会话ID在sesslist列表中的进程 |
-t ttylist | 显示终端ID在ttylist列表中的进程 |
-u userlist | 显示有效用户ID在userlist列表中的进程 |
-F | 显示更多额外输出(相对-f参数而言) |
-O format | 显示默认的输出列以及format列表指定的特定列 |
-M | 显示进程的安全信息 |
-c | 显示进程的额外调度器信息 |
-f | 显示完整格式的输出 |
-j | 显示任务信息 |
-l | 显示长列表 |
-o format | 仅显示由format指定的列 |
-y | 不要显示进程标记(process flag,表明进程状态的标记) |
-Z | 显示安全标签(security context)①信息 |
-H | 用层级格式来显示进程(树状,用来显示父进程) |
-n namelist | 定义了WCHAN列显示的值 |
-w | 采用宽输出模式,不限宽度显示 |
-L | 显示进程中的线程 |
-V | 显示ps命令的版本号 |
上面给出的参数已经很多了,不过还有很多。使用ps命令的关键不在于记住所有可用的参数,而在于记住最有用的那些参数。大多数Linux系统管理员都有自己的一组参数,他们会牢牢记住这些用来提取有用的进程信息的参数。举个例子,如果你想查看系统上运行的所有进程,可用-ef参数组合(ps命令允许你像这样把参数组合在一起)。
1 xiaoyu@ubuntu:~$ ps -ef
2 UID PID PPID C STIME TTY TIME CMD
3 root 1 0 0 05:25 ? 00:00:02 /sbin/init auto noprompt
4 root 2 0 0 05:25 ? 00:00:00 [kthreadd]
5 root 4 2 0 05:25 ? 00:00:00 [kworker/0:0H]
6 root 6 2 0 05:25 ? 00:00:00 [mm_percpu_wq]
7 root 7 2 0 05:25 ? 00:00:00 [ksoftirqd/0]
8 root 8 2 0 05:25 ? 00:00:00 [rcu_sched]
9 root 9 2 0 05:25 ? 00:00:00 [rcu_bh]
10 root 10 2 0 05:25 ? 00:00:00 [migration/0]
11 root 11 2 0 05:25 ? 00:00:00 [watchdog/0]
12 root 12 2 0 05:25 ? 00:00:00 [cpuhp/0]
截取一段,Linux系统上运行着很多进程。这个例子用了两个参数:-e参数指定显示所有运行在系统上的进程;-f参数则扩展了输出,这些扩展的列包含了有用的信息。
- UID:启动这些进程的用户。
- PID:进程的进程ID。
- PPID:父进程的进程号(如果该进程是由另一个进程启动的)。
- C:进程生命周期中的CPU利用率。
- STIME:进程启动时的系统时间。
- TTY:进程启动时的终端设备。
- TIME:运行进程需要的累计CPU时间。
- CMD:启动的程序名称。
上例子中输出了合理数量的信息,这也正是大多数系统管理员希望看到的。如果想要获得更多的信息,可采用-l参数,他会产生一个长格式输出。
1 xiaoyu@ubuntu:~$ ps -l
2 F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
3 0 S 1000 2051 2045 0 80 0 - 7373 wait pts/18 00:00:00 bash
4 0 R 1000 2249 2051 0 80 0 - 8996 - pts/18 00:00:00 ps
5 xiaoyu@ubuntu:~$
注意使用了-l参数之后多出的那些列。
- F:内核分配给进程的系统标记。
- S:进程的状态(O代表正在运行;S代表在休眠;R代表可运行,正等待运行;Z代表僵化--僵尸进程,进程已结束但父进程已不存在;T代表停止)。
- PRI:进程的优先级(越大的数字代表越低的优先级)。
- NI:谦让度值用来参与决定优先级。
- ADDR:进程的内存地址。
- SZ:假如进程被换出,所需交换空间的大致大小。
- WCHAN:进程休眠的内核函数的地址。
2、BSD风格的参数
参数 | 描述 |
T | 显示跟当前终端关联的所有进程 |
a | 显示跟任意终端关联的所有进程 |
g | 显示所有的进程,包括控制进程 |
r | 仅显示运行中的进程 |
x | 显示所有的进程,甚至包括未分配任何终端的进程 |
U userlist | 显示归userlist列表中某用户ID所有的进程 |
p pidlist | 显示PID在pidlist列表中的进程 |
t ttylist | 显示所关联的终端在ttylist列表中的进程 |
O format | 除了默认输出的列之外,还输出由format指定的列 |
X | 按过去的Linux i386寄存器格式显示 |
Z | 将安全信息添加到输出中 |
j | 显示任务信息 |
l | 采用长模式 |
o format | 仅显示由format指定的列 |
s | 采用信号格式显示 |
u | 采用基于用户的格式显示 |
v | 采用虚拟内存格式显示 |
N namelist | 定义在WCHAN列中使用的值 |
O order | 定义显示信息列的顺序 |
S | 将数值信息从子进程加到父进程上,比如CPU和内存的使用情况 |
c | 显示真实的命令名称(用以启动进程的程序名称) |
e | 显示命令使用的环境变量 |
f | 用分层格式来显示进程,表明哪些进程启动了哪些进程 |
h | 不显示头信息 |
k sort | 指定用以将输出排序的列 |
n | 和WCHAN信息一起显示出来,用数值来表示用户ID和组ID |
w | 为较宽屏幕显示宽输出 |
H | 将线程按进程来显示 |
m | 在进程后显示线程 |
L | 列出所有格式指定符 |
V | 显示ps命令的版本号 |
如你所见,Unix和BSD类型的参数有很多重叠的地方。使用其中某种类型参数得到的信息也同样可以使用另一种获得。大多数情况下,你只要选择自己所喜欢格式的参数类型就行了(比如你在使用Linux之前就已经习惯BSD环境了)。在使用BSD参数时,ps命令会自动改变输出以模仿BSD格式。下例是使用l参数的输出:
1 xiaoyu@ubuntu:~$ ps l
2 F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
3 0 1000 2051 2045 20 0 29492 4936 wait Ss pts/18 0:00 bash
4 0 1000 2354 2051 20 0 35984 1496 - R+ pts/18 0:00 ps l
5 xiaoyu@ubuntu:~$
注意,其中大部分的输出列跟使用Unix风格参数时的输出是一样的,只有一小部分不同。
- VSZ:进程在内存中的大小,以千字节(KB)为单位。
- RSS:进程在未换出时占用的物理内存。
- STAT:代表当前进程状态的双字符状态码。
许多系统管理员都喜欢BSD风格的l参数。它能输出更详细的进程状态码(STAT列)。双字符状态码能比Unix风格输出的单字符状态码更清楚地表示进程的当前状态。
第一个字符采用了和Unix风格S列相同的值,表明进程是在休眠、运行还是等待。第二个参数进一步说明进程的状态。
- <:该进程运行在高优先级上。
- N:该进程运行在低优先级上。
- L:该进程有页面锁定在内存中。
- s:该进程是控制进程。
- l:该进程是多线程的。
- +:该进程运行在前台。
从前面的例子可以看出,bash命令处于休眠状态,但同时它也是一个控制进程(在我的会话中,它是主要进程),而ps命令则运行在系统的前台。
3、GNU长参数
最后,GNU开发人员在这个新改进过的ps命令中加入了另外一些参数。其中一些GNU长参数复制了现有的Unix或BSD类型的参数,而另一些则提供了新功能。下表列出了现有的GNU长参数。
参数 | 描述 |
--deselect | 显示所有进程,命令行中列出的进程 |
--Group grplist | 显示组ID在grplist列表中的进程 |
--User userlist | 显示用户ID在userlist列表中的进程 |
--group grplist | 显示有效组ID在grplist列表中的进程 |
--pid pidlist | 显示PID在pidlist列表中的进程 |
--ppid pidlist | 显示父PID在pidlist列表中的进程 |
--sid sidlist | 显示会话ID在sidlist列表中的进程 |
--tty ttylist | 显示终端设备号在ttylist列表中的进程 |
--user userlist | 显示有效用户ID在userlist列表中的进程 |
--format format | 仅显示由format指定的列 |
--context | 显示额外的安全信息 |
--cols n | 将屏幕宽度设置为n列 |
--columns n | 将屏幕宽度设置为n列 |
--cumulative | 包含已停止的子进程的信息 |
--forest | 用层级结构显示出进程和父进程之间的关系 |
--headers | 在每页输出中都显示列的头 |
--no-headers | 不显示列的头 |
--lines n | 将屏幕高度设为n行 |
--rows n | 将屏幕高度设为n排 |
--sort order | 指定将输出按哪列排序 |
--width n | 将屏幕宽度设为n列 |
--help | 显示帮助信息 |
--info | 显示调试信息 |
--version | 示ps命令的版本号 |
1 xiaoyu@ubuntu:~$ ps --forest
2 PID TTY TIME CMD
3 2051 pts/18 00:00:00 bash
4 43622 pts/18 00:00:00 \_ ps
5 xiaoyu@ubuntu:~$
二、实时监测进程
ps命令虽然在收集运行在系统上的进程信息时非常有用,但也有不足之处:它只能显示某个特定时间点的信息。如果想观察那些频繁换进换出的内存的进程趋势,用ps命令就不方便了。
而top命令刚好适用这种情况。top命令跟ps命令相似,能够显示进程信息,但它是实时显示的,下图是top命令运行时输出的截图
第二行显示了进程概要信息——top命令的输出中将进程叫作任务(task):有多少进程处在运行、休眠、停止或是僵化状态(僵化状态是指进程完成了,但父进程没有响应)。
下一行显示了CPU的概要信息。top根据进程的属主(用户还是系统)和进程的状态(运行、空闲还是等待)将CPU利用率分成几类输出。
- PID:进程的ID。
- USER:进程属主的名字。
- PR:进程的优先级。
- NI:进程的谦让度值。
- VIRT:进程占用的虚拟内存总量。
- RES:进程占用的物理内存总量。
- SHR:进程和其他进程共享的内存总量。
- S:进程的状态(D代表可中断的休眠状态,R代表在运行状态,S代表休眠状态,T代表跟踪状态或停止状态,Z代表僵化状态)。
- %CPU:进程使用的CPU时间比例。
- %MEM:进程使用的内存占可用内存的比例。
- TIME+:自进程启动到目前为止的CPU时间总量。
- COMMAND:进程所对应的命令行名称,也就是启动的程序名。
三、结束进程
作为系统管理员,很重要的一个技能就是知道何时以及如何结束一个进程。有时进程挂起了,只需要动动手让进程重新运行或结束就行了。但有时,有的进程会耗尽CPU且不释放资源。在这两种情景下,你就需要能控制进程的命令。Linux沿用了Unix进行进程间通信的方法。
在Linux中,进程之间通过信号来通信。进程的信号就是预定义好的一个消息,进程能识别它并决定忽略还是作出反应。进程如何处理信号是由开发人员通过编程来决定的。大多数编写完善的程序都能接收和处理标准Unix进程信号。这些信号都列在了下表中。
信号 | 名称 | 描述 |
1 | HUP | 挂机 |
2 | INT | 中断 |
3 | QUIT | 结束运行 |
9 | KILL | 无条件终止 |
11 | SEGV | 段错误 |
15 | TERM | 尽可能终止 |
17 | STOP | 无条件停止运行,但不终止 |
18 | TSTP | 停止或暂停,但继续在后台运行 |
19 | CONT | 在STOP或TSTP之后恢复执行 |
在Linux中有两个命令可以向运行中的进程发出进程信号。
1、kill命令
kill命令可通过进程ID(PID)给进程发信号。默认情况下,kill命令会向命令行中列出的全部PID发送一个TERM信号,遗憾的是,你只能用进程的PID而不能用命令名,所以kill命令有时并不好用。
要发送进程信号,你必须是进程的属主或登陆为root用户。
1 xiaoyu@ubuntu:~$ kill 6447
2 bash: kill: (6447) - Operation not permitted
3 xiaoyu@ubuntu:~$
TERM信号告诉进程可能的话就停止运行。不过,如果有不服管教的进程,那它通常会忽略这个请求。如果要强制终止,-s参数支持制定其他信号(用信号名或信号值)。
从下例中看出,kill命令不会有任何输出。
1 xiaoyu@ubuntu:~$ kill 2120
2 xiaoyu@ubuntu:~$
要检查kill命令是否有效,可再运行ps或top命令,看看问题进程是否已停止。
2、killall命令
killall命令非常强大,它支持通过进程名而不是PID来结束进程。killall命令也支持通配符,这在系统因负载过大而变得很慢时很有用。
1 xiaoyu@ubuntu:~$ killall http*
2 http*: no process found
3 xiaoyu@ubuntu:~$
嗯,我这台机器没有启动任何http的进程,不过这个命令大家是可以尝试使用的