学习如何获取所需的 UNIX 进程信息。对于所有的系统管理员来说,了解自己的 UNIX 系统正在运行的进程是最基本的要求之一。标准的进程列表很有用,但是它所提供的信息格式不正确或者没有包含所需的进程或信息。能从进程列表中选择特定类型的数据将使管理任务更加容易。在本文中,您将了解到如何扩展该进程列表以改善信息的可读性,或者提供在其他地方不易获得的摘要和信息。您还将看到如何跨不同的 UNIX 平台获取进程信息的标准化的方法。
关于本系列
通常,UNIX 管理员都拥有一套他/她经常使用协助管理进程的实用程序、技巧和系统。有一些重要的实用程序、命令行链以及脚本可用来简化各种处理过程。这些工具中有一部分来自于操作系统,而大部分的技巧则来源于长期的经验和简化系统管理员工作的要求。本系列文章主要专注于最大限度地利用各种 UNIX 环境中可用的工具,包括简化异类环境中的管理任务的方法。
使用 ps
ps 命令行工具列出了正在运行的进程。该工具存在于所有的 UNIX 变种中,并且大体上都按相同的基本方式工作,即向内核请求正在运行的进程列表,然后报告进程列表及其属性,如内存使用情况、运行时间和其他详细信息。
ps 工具实际上是一个非常强大的工具,尽管许多管理员可能仅使用可用选项中的某一两项来查看所需的信息。可以利用内置命令行选项从命令中获取更多的信息,甚至可以将 ps 通过管道与其他命令结合起来,以获取所需的准确信息。
列出所有进程
即使是以 root 身份登录,ps 的标准输出也只列出了您所运行的进程。根据您的 UNIX 环境是基于 BSD 或者 AT&T,SysV UNIX 基础部分对用来获取系统中其他进程信息的基本命令行选项做出相应的变动,或者改变所显示的信息。在基于 BSD 的 UNIX 环境中,输出包括进程 ID、终端、状态、时间(在 CPU 中的执行时间,单位为秒,而不是进程开始运行的时间),以及所执行的命令,如清单 1 所示。
清单 1 列出 BSD UNIX 变种上的进程
$ ps
PID TT STAT TIME COMMAND
391 p5 S 0:00.24 /bin/bash
9165 p5 S+ 0:00.50 emacs
476 p6 S 0:01.03 /bin/bash
9299 p6 S 0:00.09 xterm
9319 p6 S 0:00.07 xterm
9423 p6 S 0:00.12 ftp atuin
9513 p6 R+ 0:00.01 ps
9301 p7 Ss+ 0:00.01 /usr/X11R6/bin/luit
9302 p8 Ss+ 0:00.03 bash
9321 p9 Ss+ 0:00.01 /usr/X11R6/bin/luit
9322 pa Ss+ 0:00.02 bash
在 SVR4 环境中,所提供的列较少(您得不到进程状态信息),如下面的清单 2 所示。
清单 2. 列出 SVR4 UNIX 变种上的进程
$ ps
PID TTY TIME CMD
19915 pts/3 00:00:00 bash
29145 pts/3 00:00:00 emacs
32256 pts/3 00:00:00 emacs
26986 pts/3 00:00:00 xterm
31303 pts/3 00:00:00 ftp
31358 pts/3 00:00:00 ps
要获取系统中正在运行的所有进程的列表,需要根据所使用的 UNIX 变种来使用不同的命令行选项。在 BSD UNIX 中,-a 命令行选项列出了包括您自己在内的所有用户的进程。然而,这个列表并不会包括那些没有控制终端的进程(比如那些在启动时开始执行的进程、守护进程以及那些作为 cron 工作的一部分的进程)。要获取所有正在运行的进程的列表,必须使用 -A 命令行选项(请参阅清单 3)。
清单 3. 列出 BSD 系统上的所有进程
$ ps -A
PID TT STAT TIME COMMAND
1 ?? S
23 ?? Ss 0:00.02 /sbin/dynamic_pager -F /private/var/vm/swapfile
27 ?? Ss 0:00.95 kextd
49 ?? Ss 0:05.17 /usr/sbin/configd
50 ?? Ss 0:01.89 /usr/sbin/coreaudiod
51 ?? Ss 0:04.40 /usr/sbin/diskarbitrationd
52 ?? Ss 0:00.08 /usr/sbin/memberd -x
53 ?? Ss 0:02.80 /usr/sbin/securityd
55 ?? Ss 11:03.59 /usr/sbin/notifyd
57 ?? Ss 0:01.13 /usr/sbin/DirectoryService
...
8051 p2 S+ 0:00.61 ssh root@bear
292 p3 Ss 0:00.02 bash
372 p3 S+ 0:00.42 ssh admin@atuin
312 p4 Ss+ 0:00.03 bash
332 p5 Ss 0:00.03 bash
391 p5 S 0:00.24 /bin/bash
9165 p5 S+ 0:00.50 emacs
352 p6 Ss 0:00.04 bash
476 p6 S 0:01.04 /bin/bash
9299 p6 S 0:00.09 xterm
9319 p6 S 0:00.07 xterm
9423 p6 S 0:00.14 ftp atuin
9520 p6 R+ 0:00.01 ps -A
9301 p7 Ss+ 0:00.01 /usr/X11R6/bin/luit
9302 p8 Ss+ 0:00.03 bash
9321 p9 Ss+ 0:00.01 /usr/X11R6/bin/luit
9322 pa Ss+ 0:00.02 bash
-A 命令行选项与同时使用 -a 和 -x 选项等效,其中 -a 显示拥有控制终端的进程,-x 显示没有控制终端的进程。
在 SVR4 变种中,显示所有正在运行的进程(不论它是否拥有控制终端)的命令行选项是 -e 。从所显示的进程来看,它和 BSD 的 -A 选项是等价的。可以在清单 4 中看到输出的示例。
清单 4. SVR4 环境中的进程列表
$ ps -e
PID TTY TIME CMD
0 ? 15:24 sched
1 ? 0:00 init
2 ? 0:00 pageout
3 ? 0:00 fsflush
308 ? 0:00 devfsadm
7 ? 0:06 svc.star
9 ? 0:10 svc.conf
506 ? 0:00 htt_serv
260 ? 0:00 rpcbind
259 ? 0:00 cron
52 ? 0:00 dhcpagen
282 console 0:00 ttymon
267 ? 0:00 lockd
264 ? 0:00 statd
90 ? 0:00 sysevent
...
462 ? 0:00 smcboot
464 ? 0:00 smcboot
463 ? 0:00 smcboot
473 ? 0:00 htt
552 ? 0:00 in.telne
527 ? 0:00 dmispd
548 ? 0:01 snmpd
输出的区别在于所显示的信息列,但是可以通过指定所需要的列来进行修改。
列出指定信息
ps 工具包含许多标准的显示列集。例如在 SVR4 中,经常使用 ps -ef 来获取关于列出进程的更详细的信息,包括父进程 ID、处理器利用率、开始时间以及更详细的命令行,如清单 5 所示。
清单 5. 扩展输出
ps -ef
UID PID PPID C STIME TTY TIME CMD
root 0 0 0 15:56:26 ? 15:24 sched
root 1 0 0 15:56:26 ? 0:00 /sbin/init
root 2 0 0 15:56:26 ? 0:00 pageout
root 3 0 0 15:56:26 ? 0:00 fsflush
root 308 1 0 15:57:09 ? 0:00 devfsadmd
root 7 1 0 15:56:29 ? 0:06 /lib/svc/bin/svc.startd
...
root 562 1 0 15:58:17 ? 0:00 /usr/lib/sendmail -bd -q15m
root 576 555 1 16:01:47 pts/1 0:00 ps -ef
root 416 1 0 15:57:14 ? 0:00 /usr/sbin/syslogd
smmsp 561 1 0 15:58:17 ? 0:00 /usr/lib/sendmail -Ac -q15m
...
root 552 283 0 15:57:47 ? 0:00 /usr/sbin/in.telnetd
root 527 1 0 15:57:22 ? 0:00 /usr/lib/dmi/dmispd
root 548 1 0 15:57:24 ? 0:01 /usr/sfw/sbin/snmpd
对于 BSD 环境,通常添加 -l 选项,它为每个进程产生“长”输出,如清单 6 所示。
清单 6. 在 BSD 中显示更详细的信息
$ ps -al
UID PID PPID CPU PRI NI VSZ RSS WCHAN STAT TT TIME COMMAND
0 9165 391 0 31 0 57896 6376 - S+ p5 0:00.50 emacs
501 352 349 0 31 0 27784 52 - Ss p6 0:00.04 bash
0 476 352 0 31 0 27784 600 - S p6 0:01.05 /bin/bash
0 9299 476 0 31 0 44988 1880 - S p6 0:00.09 xterm
0 9319 476 0 31 0 44988 1888 - S p6 0:00.07 xterm
0 9423 476 0 31 0 27504 488 - S p6 0:00.15 ftp atuin
0 9540 476 0 31 0 27384 504 - R+ p6 0:00.01 ps -axl
0 9301 9299 0 31 0 27332 452 - Ss+ p7 0:00.01 /usr/X11R6/bin/luit
0 9302 9301 0 31 0 27784 888 - Ss+ p8 0:00.03 bash
0 9321 9319 0 31 0 27332 452 - Ss+ p9 0:00.01 /usr/X11R6/bin/luit
0 9322 9321 0 31 0 27784 888 - Ss+ pa 0:00.02 bash
这些选项存在的问题是,尽管它们显示了更多的信息,但这些信息并不总是特别有用,或者在寻找某个特定进程时包含了不希望看到的扰乱显示的信息。
幸运的是,所有版本的 ps 还包括了指定要显示的列的功能。可以广泛地使用这种命令,不管是仅提取所需要的信息,还是在异类环境中使用它来创造标准化的跨不同 UNIX 环境的输出。
要使用这一特性,可使用 -o 命令行选项,并列出要显示的列,以逗号做列的分隔符。尽管可选择的列的范围有一些差别,但在不同的 UNIX 版本中,大部分列都是标准的。例如,所有变种中都有 pid、ppid(父进程 ID)、command、RSS(驻留集大小或内存使用情况)以及 priority。
使用时,可以用 -o 来选择列及其显示顺序。例如,要获取 pid、ppid 和 commond,在 BSD 中可以使用 -opid,ppid,command,如清单 7 所示,或者在 SVR4 中使用 -opid,ppid,comm,如清单 8 所示。
清单 7. 在 BSD 中选择特定的列
$ ps -o pid,ppid,command
PID PPID COMMAND
391 332 /bin/bash
9165 391 emacs
清单 8. 在 SVR4 中选择特定的列
$ ps -opid,ppid,comm
PID PPID COMMAND
555 552 -sh
622 555 ps
选择了想要的列后,可能会选择信息的排列顺序。ps 的缺省设置是根据进程 ID 来排列进程列表,但是这样可能会隐藏正在查找的信息。当查找内存饥饿的进程时,按内存使用量来排列显示结果比按进程 ID 更加有用。
有些 ps 变种通过使用命令行选项来支持这种情况。BSD 变种中使用 -m 选项来按内存使用情况排序,或者使用 -r 来按 CPU 使用情况排序。许多 SVR4 变种没有该问题的明确解决办法,但是可以在所有环境中通过将 ps 和 sort 进行组合来生成类似的结果。例如,要获取按 CPU 使用量排序的进程列表,可以在 BSD 中使用清单 9 所示的命令。
清单 9. 在 BSD 中跟踪 CPU 使用情况
$ ps -A -o pid,%cpu,command|sort -n +1
...
358 0.1 ftp
11425 0.1 /bin/bash
28684 0.3 trivial-rewrite -n rewrite -t unix -u
356 0.4 ssh
354 0.5 as
23988 1.1 emacs
136 14.6 cc1plus
26306 23.6 cpp
在 SVR4 中,需要将 %cpu 改为 pcpu,而该命令在其他方面是相同的(请参见清单 10)。
清单 10. 在 SVR4 中跟踪 CPU 使用情况
$ ps -e -opid,pcpu,comm|sort -n +1
...
3 0.1 fsflush
555 0.1 -sh
627 0.2 sort
628 0.2 ps
该命令链是可行的,因为已经指定了进程列表的排列顺序,因此可以按照这些列进行排序以获取真正想要的信息。如果想使用不同的标准来查找进程,还有其他的可用方案。
列出特定进程
显然,在获取了正在运行的进程列表之后,也许希望列出特定的进程。一个显而易见的方法就是将 ps 的输出与 grep 结合起来,以提取所需的信息。在一些 UNIX 变种中,可以通过一些特定的实用程序来完成这项工作,例如 pgrep,但是如果要查找一些特定的命令,使用 grep 也同样是很有效的。
ps 命令还支持根据更明确的标准来显示进程,例如用户 ID、父进程或控制终端。例如,-U 命令行选项限定了进程列表只显示指定用户名的进程。例如,要显示当前属于 root 用户的进程,请参阅清单 11。
清单 11. 根据用户列出进程
$ ps -U root
PID TTY TIME CMD
0 ? 15:24 sched
1 ? 0:00 init
2 ? 0:00 pageout
3 ? 0:02 fsflush
308 ? 0:00 devfsadm
7 ? 0:06 svc.star
...
552 ? 0:00 in.telne
527 ? 0:00 dmispd
629 pts/1 0:00 ps
548 ? 0:01 snmpd
要获取指定终端的所有进程,使用 -t,如清单 12 所示。
清单 12. 根据终端列出进程
$ ps -t 3
PID TTY TIME CMD
19915 pts/3 00:00:00 bash
29145 pts/3 00:00:00 emacs
32256 pts/3 00:00:00 emacs
在有了这些信息后,也许想要用它来对进程进行相应的操作。
向多个线程发送信号
当找到了要查询的进程后,最常用的命令之一是 kill。它向一个或多个进程发送特定的信号。对于启动了多个线程或子线程的守护进程的情况,可以尝试向父进程发送信号,以此向所有的进程发送信号。然而,这种方法并不是对所有的守护进程和应用程序都有效。
显然,您希望避免手动挑选出这些进程。有些 UNIX 变种中有一个名为 pkill 的工具,它能够向满足特定模式或其他标准的进程(例如终端、进程组、用户 ID 以及组 ID 列表)发送相同的信号。
可以通过链接 ps、grep、awk、xargs 以及 kill 命令的方式来模拟这一基本操作,向满足特定命令模式的多个进程发送信号。例如,要向所有命令中包含“httpd”的进程发送 kill 信号,可以使用:
-
$ ps -e -opid,command |grep httpd|awk '{print $1}'|xargs kill -9
复制代码
如果对单个元素进行研究,将更容易理解它。
该命令显示了所有正在运行的进程的列表(这是在 SVR4 系统中,而在 BSD 中则应使用 -A)。它仅显示了进程 ID 和被执行的命令。不需要任何其他的信息,而且使用更详细的输出可能会引入其他方面符合搜索条件的文本。
-
$ ps -e -opid,command |grep httpd
复制代码
该命令仅提取了命令中含有 httpd 的进程(因为进程列表中产生的唯一其他信息是进程 ID):
-
$ ps -e -opid,command |grep httpd|awk '{print $1}'
复制代码
通过使用 awk,仅筛选除去打印输出中的第一个参数,即进程 ID。
-
$ ps -e -opid,command |grep httpd|awk '{print $1}'|xargs kill -9
复制代码
xargs 命令接收空白字符分隔的项目列表(其中空白字符包括回车、换行、制表符以及一个或多个空格),并把它格式化为参数列表传递给指定的命令,在本示例中是 kill 命令。
最好把它放进脚本程序中,并取一个适当的名字,例如(pkill 或者 killbyname)。可以设置该脚本接收两个参数,信号和匹配文本,甚至还可以考虑操作系统的差别,如清单 13 所示。
清单 13. 通过命令字符串向进程发送消息
-
#!/bin/sh
-
-
HOSTTYPE=`uname -s`
-
-
SIGNAL=$1
-
STRING=$2
-
-
if [ -z "$1" -o -z "$2" ]
-
then
-
echo Usage: $0 signal string
-
exit 1
-
fi
-
-
case $HOSTTYPE in
-
Darwin|BSD)
-
ps -a -opid,command | grep $STRING | awk '{ print $1; }' | xargs kill $SIGNAL
-
;;
-
Linux|Solaris|AIX|HP-UX)
-
ps -e -opid,command | grep $STRING | awk '{ print $1; }' | xargs kill $SIGNAL
-
;;
-
esac
复制代码
这里所显示的基本技术可以用于其他类似的排序规则。
计算内存使用情况
ps 工具还提供了我们目前尚未涉及的两个列。RSS 列提供了进程的“驻留集大小”,这是该进程所使用的物理内存量,也是进程占用多少实际内存的指示。VSZ 列详细列出进程正在使用的内存总量,包括所分配的内部存储,但通常已被交换到磁盘。对于大多数 ps 变体而言,这两个列都是比较常用的。
确定这两个数据能够更好地了解内存的使用情况。如果将 ps 与 grep 组合起来选择特定的进程,并使用 awk 来计算总量,就可以获取单个应用程序或者某个应用程序及其子进程正占用多少物理内存和虚拟内存。
例如,要确定 bash 进程所使用的物理内存和虚拟内存,可以使用清单 14 中的命令。
清单 14. 使用 ps 和 awk 计算内存使用情况
-
$ ps -A -o rss,vsz,command|grep bash | \
-
awk '{rss += $1; vsz += $2 } END { print "Real: ",rss, "Virtual: ",vsz }'
-
Real: 4004 Virtual: 305624
复制代码
这在诊断内存和交换区使用情况的问题时特别有用。
使用与作业控制兼容的 Shell
对于一个典型的系统管理员来说,在任一时刻运行一或两个以上的特定任务是很平常的事。尽管在任何时刻与服务器的连接可能不止一个,不论是多终端窗口(例如通过 xterm)还是其他的终端,或者通过 SSH、Telnet 的远程连接,有时候需要在活动的 Shell 或者环境中控制或监视多个进程。
所有的 Shell 都支持在命令的末尾附加连字符 (&) 来使命令自动在后台运行。但有时希望将一个交互应用程序(如一个编辑器)放到后台,以便可以运行一个 Shell 命令,然后返回到编辑器会话。
这种控制后台进程的能力称为作业控制,它是 Korn Shell、C Shell 以及开源的 Shell(例如 bash 和 zsh)的标准特性。
为了实现在 Shell 中每次启动一个命令时在后台运行的基本作业控制,命令(可以是任何合法的命令行,甚至是内联脚本)给出了作业引用 ID。
-
$ find / -name "core" >/tmp/corelist 2>&1 &
-
[3] 11957
复制代码
可以使用 jobs 命令获取正在后台运行的作业列表,如清单 15 所示。
清单 15. 使用 jobs 命令
-
$ jobs
-
[1]- Stopped emacs MCSLP/Intranet/News.pm
-
[2]+ Stopped emacs MCSLP/Intranet/Media.pm
-
[3] Running find / -name "core" >/tmp/corelist 2>&1 &
复制代码
在该清单中,第二个 emacs 命令使用了 + 号标注。这表示 Shell 认为它是当前的活动作业。先前启动的 find 不是活动的工作,因为它不需要进行交互(尽管它产生输出,它并不需要输入来继续),因而不是活动的进程。第一个 emacs 进程使用了 -- 标注,说明 Shell 认为它先前是活动的命令。可以分别使用 %+ 和 %- 字符串来引用这些作业。
可以通过在 fg 后输入作业编号或者作业字符串(%+、%-)来将任何正在运行的作业切换为前台进程。如果省略了引用,Shell 就切换到当前的活动工作。
要挂起当前运行的进程,按 Control-Z。可以使用下面的代码进行重新配置:
-
ftp>
-
[3]+ Stopped ftp atuin
复制代码
它可以和许多不同的命令及应用程序一起使用。对于在 Shell 中运行的大部分简单命令,如 ls 或者 find,它也应该是有效的。请注意,该作业被标记为 Stopped。这意味着已经暂停了该命令的执行。要将该命令切换为后台进程,使用 bg 命令。与 fg 一样,bg 接受作业引用或者在不带参数时缺省为当前活动的作业作为其参数。如果该命令需要输入(如编辑器、FTP 等),在 bg 命令之后再次按下回车时,会被警告该进程已暂时挂起(请参见清单 16)。
清单 16. 进程被暂时挂起的警告
-
$ bg
-
[3]+ ftp atuin &
-
$
-
[3]+ Stopped
复制代码
请注意,如果后台命令产生了输出,并且没有将其重定向,那么即使通过作业控制将该命令放到后台,它依然会输出到屏幕。
在一个繁忙的环境中,作业控制就成了管理和控制多个后台作业,或者快速退出编辑器和其他命令并返回到活动 Shell(相对与运行一个新的 Shell)的最简便方法之一。
在后台可靠地运行进程
有时会想要在后台运行脚本、实用程序或命令行。然而,大部分系统在用户断开连接或注销时将终止在后台运行的命令,如果想要先登录,启动命令,然后再注销,这时会发现并不是想要的结果。
如果需要重新启动,或者重新初始化后台或没有自动进行守护的守护进程,或者依赖于独立的脚本来开始和管理守护应用程序的执行,在这些情况下命令执行的终止是非常让人沮丧的。MySQL mysqld_safe 脚本是一个很好的例子,它正是以这种方式进行工作的。
要防止应用程序在您注销时自动终止,可以使用 nohup 命令作为要运行的命令行或工具的前缀,如下所示:
除非专门为命令的输出进行重定向,否则 nohup 自动将标准输出和标准错误写入当前目录下名为 nohup.out 的文件中。
使用标准重定向可以输出到自己的文件,但请记住,要同时重定向输出和错误流,例如:
-
$ nohup find/ -name core >/tmp/corefind.out 2>&1
复制代码
我发现自己总是无意识地使用 nohup 来运行任何我认为持续时间将长于 2、3 分钟的命令,即使是在控制台中运行该命令。这可能在很大程度上和输出的自动重定向有关,而不是在连接失败时防止终止的能力。
阅读(1181) | 评论(0) | 转发(0) |