9.2 收集进程信息

进程是程序的运行实例(running instance)。运行在一台计算机中的多个进程各自都分配了一个称为进程ID的唯一标识数字。这个数字是一个整数。同一个程序的多个实例可以同时运行,但是他们的进程ID却互不相同。一个进程包括多种属性,例如拥有该进程的用户、进程使用的内存数量、进程占用的CPU等。这则攻略将学习如何收集有关进程的信息。

9.2.1 新手上路

和进程管理相关的重要命令是toppspgrep。让我们看看如何收集进程信息。

9.2.2 实战演练

ps是收集进程信息的重要工具。它提供的信息包括:拥有进程的用户、进程的起始时间、进程所对应的命令行路径、进程ID(PID)、进程所属的终端(TTY)、进程使用的内存、进程占用的CPU等。例如:

  1. $ ps
  2. PID TTY TIME CMD
  3. 1220 pts/0 00:00:00 bash
  4. 1242 pts/0 00:00:00 ps

ps命令通常结合一系列参数使用。如果不使用任何参数,ps将显示运行在当前终端(TTY)中的进程。第一列显示进程ID(PID),第二列是TTY(终端),第三列是进程启动后过去的时间,最后一列是CMD(进程所对应的命令)。

为了显示包含更多信息的更多列,可以使用-f,如下所示。

  1. $ ps -f
  2. UID PID PPID C STIME TTY TIME CMD
  3. slynux 1220 1219 0 18:18 pts/0 00:00:00 -bash
  4. slynux 1587 1220 0 18:59 pts/0 00:00:00 ps -f

上面的ps命令没有什么用处,因为它没有提供当前终端外的任何进程信息。要获取运行在系统中的每一个进程的信息,使用选项 -e(every)。选项-ax(all)也可以生成同样的输出。

9.2 收集进程信息 - 图1 选项-x-a指定解除由ps默认添加的TTY限制。通常使用不带参数的ps命令,以便只打印出其所属终端的进程。

运行ps -eps -ef,要么是ps -axps -axf

  1. $ ps -e | head
  2. PID TTY TIME CMD
  3. 1 ? 00:00:00 init
  4. 2 ? 00:00:00 kthreadd
  5. 3 ? 00:00:00 migration/0
  6. 4 ? 00:00:00 ksoftirqd/0
  7. 5 ? 00:00:00 watchdog/0
  8. 6 ? 00:00:00 events/0
  9. 7 ? 00:00:00 cpuset
  10. 8 ? 00:00:00 khelper
  11. 9 ? 00:00:00 netns

输出列表很长。我们使用head进行了过滤,所以只列出了前10项。

ps命令支持显示除进程名及进程ID之外的多种信息。ps默认在不同的列中显示这些信息。这些信息中的大多数对我们来说没什么用处。我们可以用-o来指定想要显示的列。这样就可以只打印出我们需要的内容。与进程相关的参数可以通过与此参数对应的命令选项指定。参数列表以及-o的用法在接下来会进行讨论。

用ps显示需要的输出列:

  1. $ ps [OTHER OPTIONS] -o parameter1,parameter2,parameter3 ..

9.2 收集进程信息 - 图2 -o的参数以逗号操作符(,)作为定界符。值得注意的是逗号操作符与它分隔的参数之间是没有空格的。在大多数情况下,选项-o都是和选项-e(every)结合使用的(-oe),因为它需要列出运行在系统中的每一个进程。但是如果-o需要使用某些过滤器,例如列出特定用户拥有的进程,那么就不再使用-e-e和过滤器结合使用将没有任何实际效果,依旧会显示所有的进程。

示例如下。其中comm表示COMMAND,pcpu表示CPU占用率:

  1. $ ps -eo comm,pcpu | head
  2. COMMAND %CPU
  3. Init 0.0
  4. kthreadd 0.0
  5. migration/0 0.0
  6. ksoftirqd/0 0.0
  7. watchdog/0 0.0
  8. events/0 0.0
  9. cpuset 0.0
  10. khelper 0.0
  11. netns 0.0

选项-o可以使用不同的参数,这些参数及其描述如表9-1所示。

表 9-1

参  数 描  述
pcpu CPU占用率
pid 进程ID
ppid 父进程ID
pmem 内存使用率
comm 可执行文件名
cmd 简单命令(simple command) 1
user 启动进程的用户
nice 优先级(niceness)
time 累计的CPU时间
etime 进程启动后度过的时间
tty 所关联的TTY设备
euid 有效用户ID
stat 进程状态

1 简单命令是我们平时使用最频繁的一种命令。它是由空白字符分隔的一系列单词,以shell控制操作符作为结尾。第一个单词指定要执行的命令,余下的单词作为命令参数。Shell控制操作符可以是换行符,或者是:||,&&,&,;,;;,|,|&,(,)

9.2.3 补充内容

让我们看看其他进程控制命令的用例。

  • top

top对于系统管理员来说是一个极为重要的命令。top命令默认会输出一个占用CPU最多的进程列表。该命令的用法如下:

  1. $ top

除了若干个占用CPU最多的进程外,该命令还会显示一些其他参数。

  • 根据参数对ps输出进行排序

可以用--sortps命令的输出根据特定的列进行排序。

可以在参数前加上+(升序)或-(降序)来指定排序方式:

  1. $ ps [OPTIONS] --sort -paramter1,+parameter2,parameter3..

例如,要列出占用CPU最多的10个进程,可以使用:

  1. $ ps -eo comm,pcpu --sort -pcpu | head
  2. COMMAND %CPU
  3. Xorg 0.1
  4. hald-addon-stor 0.0
  5. ata/0 0.0
  6. scsi_eh_0 0.0
  7. gnome-settings- 0.0
  8. init 0.0
  9. hald 0.0
  10. pulseaudio 0.0
  11. gdm-simple-gree 0.0

进程依据CPU占用率进行降序排序,用head命令提取前10个进程。

我们可以用grepps的输出中提取与给定进程名或其他参数相关的条目。要找出与bash进程相关的条目,可以使用:

  1. $ ps -eo comm,pid,pcpu,pmem | grep bash
  2. bash 1255 0.0 0.3
  3. bash 1680 5.5 0.3
  • 找出给定命令名对应的进程ID

假设一个命令有多个实例正在运行,我们可能需要识别这些进程的进程ID。这个信息可以使用pspgrep命令得到。按照下面的方式使用ps

  1. $ ps -C COMMAND_NAME

或者

  1. $ ps -C COMMAND_NAME -o pid=

用户自定义格式指示符-o先前已经讲解过了。但是这里你可以看到pid后面加上了 =,这将移除 ps 输出中的头部 PID。在参数后加上=就可以移除每一列的头部。例如:

  1. $ ps -C bash -o pid=
  2. 1255
  3. 1680

这条命令列出了所有Bash进程的进程ID。

除此之外,还有一个很方便的工具pgrep。你可以用它获得一个特定命令的进程ID列表。例如:

  1. $ pgrep COMMAND
  2. $ pgrep bash
  3. 1255
  4. 1680

9.2 收集进程信息 - 图3 pgrep只需要命令名的一部分作为输出参数来提取Bash命令,诸如pgrep ashpgrep bas都能够奏效,但是ps需要你输入命令准确的全名。

pgrep可以接受很多输出过滤选项。如果要指定输出定界符,而不以换行符作为定界符,可以这样:

  1. $ pgrep COMMAND -d DELIMITER_STRING
  2. $ pgrep bash -d ":"
  3. 1255:1680

指定进程的用户(拥有者)列表:

  1. $ pgrep -u root,slynux COMMAND

其中root和slynux都是用户名。

返回所匹配的进程数量:

  1. $ pgrep -c COMMAND
  • 根据真实用户或ID以及有效用户或ID过滤ps输出

可以用ps根据指定的真实/有效用户名或ID对进程进行分组。指定的参数可以用来过滤ps的输出:通过检查每一个输出条目是否属于参数列表中指定的有效用户或真实用户,并只显示匹配的条目。实现方法如下:

  • -u EUSER1EUSER2依次类推,指定有效用户列表;

  • -U RUSER1RUSER2依次类推,指定真实用户列表。

例如:

  1. $ ps -u root -U root -o user,pcpu

该命令会显示以root作为有效用户ID和真实用户ID的所有进程,以及用户、CPU占用率列。

9.2 收集进程信息 - 图4 在大多数情况下,-o都是和-e结合使用的,并写成-eo的形式。但是当使用过滤器的时候,-o应该像上面那样单独使用。

  • 用TTY过滤ps输出

可以通过指定进程所属的TTY选择ps的输出。用选项 -t指定TTY列表:

  1. $ ps -t TTY1, TTY2 ..

例如:

  1. $ ps -t pts/0,pts/1
  2. PID TTY TIME CMD
  3. 1238 pts/0 00:00:00 bash
  4. 1835 pts/1 00:00:00 bash
  5. 1864 pts/0 00:00:00 ps
  • 进程线程的相关信息

通常,与进程线程相关的信息在ps输出中是看不到的。我们可以用选项 -Lps输出中显示线程的相关信息。这会显示出两列:NLWP和NLP。NLWP是进程的线程数量,NLP是ps输出中每个条目的线程ID。例如:

  1. $ ps -eLf

或者

  1. $ ps -eLf --sort -nlwp | head
  2. UID PID PPID LWP C NLWP STIME TTY TIME CMD
  3. root 647 1 647 0 64 14:39 ? 00:00:00 /usr/sbin/
  4. console-kit-daemon --no-daemon
  5. root 647 1 654 0 64 14:39 ? 00:00:00 /usr/sbin/
  6. console-kit-daemon --no-daemon
  7. root 647 1 656 0 64 14:39 ? 00:00:00 /usr/sbin/
  8. console-kit-daemon --no-daemon
  9. root 647 1 657 0 64 14:39 ? 00:00:00 /usr/sbin/
  10. console-kit-daemon --no-daemon
  11. root 647 1 658 0 64 14:39 ? 00:00:00 /usr/sbin/
  12. console-kit-daemon --no-daemon
  13. root 647 1 659 0 64 14:39 ? 00:00:00 /usr/sbin/
  14. console-kit-daemon --no-daemon
  15. root 647 1 660 0 64 14:39 ? 00:00:00 /usr/sbin/
  16. console-kit-daemon --no-daemon
  17. root 647 1 662 0 64 14:39 ? 00:00:00 /usr/sbin/
  18. console-kit-daemon --no-daemon
  19. root 647 1 663 0 64 14:39 ? 00:00:00 /usr/sbin/
  20. console-kit-daemon --no-daemon

该命令列出了线程数最多的10个进程。

  • 指定输出宽度以及所要显示的列

我们可以使用用户自定义输出格式指示符来指定在ps输出中所要显示的列。另一种指定输出格式的方法是使用“标准”选项。根据你的使用方式进行应用,可以尝试以下选项:

  • -f ps -ef

  • u ps -e u

  • ps ps -e ww代表宽松输出)

  • 显示进程的环境变量

了解某个进程依赖哪些环境变量,这类重要的信息通常是我们用得着的。无论进程的正常运行是否特别依赖于一组环境变量。我们都可以利用环境变量调试、修复与进程运行相关的问题。

要伴随ps条目同时列出环境变量,可以使用:

  1. $ ps -eo cmd e

例如:

  1. $ ps -eo pid,cmd e | tail -n 3
  2. 1162 hald-addon-acpi: listening on acpid socket /var/run/acpid.socket
  3. 1172 sshd: slynux [priv]
  4. 1237 sshd: slynux@pts/0
  5. 1238 -bash USER=slynux LOGNAME=slynux HOME=/home/slynux PATH=/usr/
  6. local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
  7. MAIL=/var/mail/slynux SHELL=/bin/bash SSH_CLIENT=10.211.55.2 49277 22
  8. SSH_CONNECTION=10.211.55.2 49277 10.211.55.4 22 SSH_TTY=/dev/pts/0
  9. TERM=xterm-color LANG=en_IN XDG_SESSION_COOKIE=d1e96f5cc8a7a3bc3a0a73e44c
  10. 95121a-1286499339.592429-1573657095

这种环境跟踪方法的用途之一就是解决apt-get软件包管理器的故障。如果你是用HTTP代理连接Internet,你可能得设置环境变量http_proxy=host:port。但是有时候即使设置了代理服务器,但apt-get却弃之不用,照旧返回一个错误。那么你就要查看环境变量,跟踪这个问题。

我们可能需要借助crontab这类调度工具来使一些应用程序能够自动运行。但是这些应用也许要依赖某些环境变量。假设我们需要在某个特定时间打开一个窗口化的GUI应用程序。我们用crontab将其调度到指定的时间。然而,如果出现像下面这样的条目,你就会发现这个应用程序并没有按时启动:

  1. 00 10 * * * /usr/bin/windowapp

这是因为窗口化的应用程序总是依赖于环境变量DISPLAY。环境变量需要传递给应用程序。

首先手动运行windowapp,然后运行ps -C windowapp -eo cmd e

找出环境变量,将其添加到crontab中的命令名称之前,这个问题就可以搞定了。

按照下面的方法修改条目:

  1. 00 10 * * * DISPLAY=:0 /usr/bin/windowapp

DISPLAY=:0 可以从ps输出中获取。

9.2.4 参考

9.8节讲解了如何调度任务。