1. 概述

ps 命令(“process status”的简称)用于显示和监控当前正在运行的进程

2. 命令语法

ps语法如下:

ps [options]
$ ps 
  PID TTY          TIME CMD
14900 pts/1    00:00:00 bash
14925 pts/1    00:00:00 ps

默认情况下它会以四列的形式打印当前用户和终端的进程

  • PID – 进程ID
  • TTY – 进程关联的终端
  • TIME – 进程的累计CPU使用时间
  • CMD – 执行的命令

3. 基础用法

3.1. 列出所有进程

使用 -e 可打印系统中的所有进程,而不仅仅是当前终端的进程:

$ ps -e
  PID TTY          TIME CMD
    1 ?        00:00:25 systemd
    2 ?        00:00:00 kthreadd
    3 ?        00:00:00 rcu_gp
  ...

我们还可以 使用 -f 查看更详细的输出

$ ps -e -f
  UID        PID  PPID  C STIME TTY          TIME CMD
  root         1     0  0 Feb11 ?        00:00:26 /usr/lib/systemd/systemd --switched-root --system --deserialize 28
  root         2     0  0 Feb11 ?        00:00:00 [kthreadd]
  root         3     2  0 Feb11 ?        00:00:00 [rcu_gp]
  ...

每一列的含义:

  • UID – 进程所有者的用户ID
  • PPID – 父进程ID(在这个特定的片段中,rcu_gp 是由 kthread 派生的)
  • C – CPU使用率百分比
  • STIME – 进程的启动时间

此外,当 ps 能够识别进程参数时,它还会在 CMD 列中打印这些参数。

3.2. 搜索过滤

使用 -C 选项按名称搜索特定进程

$ ps -C systemd
  PID TTY          TIME CMD
    1 ?        00:00:06 systemd
 1658 ?        00:00:00 systemd

注意,我们使用完整的进程可执行文件名,而不是子字符串。

此外,ps 还允许我们 使用 -p 根据进程 ID 列表进行过滤

$ ps -p 1,1658
  PID TTY          TIME CMD
    1 ?        00:00:06 systemd
 1658 ?        00:00:00 systemd

使用 -u 按用户名搜索:

$ ps -u root
  PID TTY          TIME CMD
    1 ?        00:00:08 systemd
    2 ?        00:00:00 kthreadd
    3 ?        00:00:00 rcu_gp
   ...

这里有一个特别需要注意的地方,我们将在下一节详细解释。

4. 高级用法

4.1. 显示线程

使用-L参数,列出指定进程创建的线程:

$ ps -C gedit -L 
  PID   LWP TTY          TIME CMD
12050 12050 ?        00:00:02 gedit
12050 12051 ?        00:00:00 gmain
12050 12052 ?        00:00:00 gdbus
12050 12054 ?        00:00:00 dconf worker

-C 按进程名称进行过滤,这里搜索名为 "gedit" 的进程。

LWP 列表示线程ID。

如果我们想要更多的输出,可结合-f使用:

$ ps -C gedit -L -f 
  UID       PID  PPID   LWP  C NLWP STIME TTY          TIME CMD
  user    12050  1658 12050  0    4 21:36 ?        00:00:03 /usr/bin/gedit --gapplication-service
  user    12050  1658 12051  0    4 21:36 ?        00:00:00 /usr/bin/gedit --gapplication-service
  user    12050  1658 12052  0    4 21:36 ?        00:00:00 /usr/bin/gedit --gapplication-service
  user    12050  1658 12054  0    4 21:36 ?        00:00:00 /usr/bin/gedit --gapplication-service

NLWP 列表示 进程的总线程数

4.2. 列出子进程

如需 查看衍生的子进程 而不是线程。我们可以 使用 -H 选项:

$ ps -e -H
  PID TTY          TIME CMD
 ...
 2493 ?        00:01:36       firefox
 2562 ?        00:00:03         Web Content
 2614 ?        00:00:09         WebExtensions
 2688 ?        00:00:32         Web Content
 2730 ?        00:00:33         Web Content
 2949 ?        00:00:09         Web Content
 ...

在这个特定的例子中,我们可以看到系统中所有进程的层次结构。不幸的是,我们无法直接通过进程ID或进程名称进行过滤

然而,我们可以通过会话ID(SID)进行过滤。要获取会话ID,我们需要修改 ps 命令的输出方式。我们将在下一节详细解释这是如何工作的。

首先,让我们获取我们进程的会话ID:

$ ps -C firefox -o pid,sid,cmd
  PID   SID CMD
 2493  1874 /usr/lib64/firefox/firefox

然后,我们可以使用 -g 标志按 SID 进行过滤,以获得更小的输出:

$ ps -g 1874 -H 
  PID TTY          TIME CMD
 2125 ?        00:00:00 ibus-x11
 1874 ?        00:00:46 gnome-shell
 1919 ?        00:00:20   Xwayland
 2116 ?        00:00:02   ibus-daemon
 2122 ?        00:00:00     ibus-dconf
 2123 ?        00:00:00     ibus-extension-
 2214 ?        00:00:01     ibus-engine-sim
 2493 ?        00:01:58   firefox
 2562 ?        00:00:04     Web Content
 2614 ?        00:00:10     WebExtensions
 2688 ?        00:00:36     Web Content
 2730 ?        00:00:36     Web Content
 2949 ?        00:00:09     Web Content

让我们仔细看看这个输出。会话ID等于启动该会话的进程ID — 也称为会话leader。在这个例子中,会话领导者是 gnome-shell 进程。

会话leader衍生了几个子进程,包括我们感兴趣的进程。现在我们可以在树状视图输出中看到它们。

4.3. 控制输出

到目前为止,我们只看到了默认输出和详细输出。在某些情况下这并不是很有帮助,特别是当我们想要使用其他工具(如 read命令)自动处理 ps 的输出时。

我们可以使用 -o 标志来控制打印哪些列:

$ ps -C gedit -L -o pid,lwp,time,comm
  PID   LWP     TIME COMMAND
12050 12050 00:00:03 gedit
12050 12051 00:00:00 gmain
12050 12052 00:00:00 gdbus
12050 12054 00:00:00 dconf worker

我们还可以 控制打印这些列的顺序

$ ps -C gedit -L -o lwp,comm,time,pid
  LWP COMMAND             TIME   PID
12050 gedit           00:00:03 12050
12051 gmain           00:00:00 12050
12052 gdbus           00:00:00 12050
12054 dconf worker    00:00:00 12050

ps 命令支持大量的输出修饰符。要查看它们的完整描述,我们可以随时查阅 Linux 帮助手册

由于修饰符很多,让我们只尝试一些比较有趣的:

$ ps -C gedit -o comm,pid,rss,pmem,stat,vsz
COMMAND           PID   RSS %MEM STAT     VSZ
gedit           12050 63072  0.1 Sl    371060

让我们解释一下这个输出:

  • RSS 表示使用的非交换物理内存,单位为千字节
  • %MEM 是进程使用的物理内存百分比(RSS 与系统总物理内存的比率)
  • STAT 是多字符进程状态(在这种情况下,进程是多线程的并且处于可中断睡眠状态)
  • VSZ 表示进程的虚拟内存大小,单位为KiB

4.4. 有效用户名和真实用户名

还记得我们之前讨论过按用户名过滤吗?

说到进程,在Linux中,我们区分两种类型的用户名:真实用户名和有效用户名

真实用户名是启动进程的用户。有效用户是拥有该进程背后可执行文件的用户。

让我们在另一个终端中运行passwd工具,并让它等待我们的提示:

$ passwd
Changing password for user.
Current password:

现在,让我们看看如果我们用 ps 打印出真实和有效用户名会发生什么:

$ ps -C passwd -o pid,tty,ruser,user,cmd
  PID TT       RUSER    USER     CMD
12503 pts/2    user     root     passwd

真实用户与有效用户不同。这是因为 passwdroot 所有,但是被我们的 user 调用。

这种特殊行为适用于兼容 setuid 的可执行文件。它发生在当前用户需要以临时提升的权限运行程序时

ps 命令可以使用 -U 选项按真实用户名过滤,也可以使用 -u 选项按有效用户名过滤

5. 结论

在这个快速教程中,我们学习了如何使用 ps 命令。

我们首先介绍了按名称、进程 ID 和用户过滤进程的基础知识。然后,我们深入探讨了进程树和线程等高级主题。

最后,我们探索了如何自定义输出,并解释了不同类型的用户名。