在Linux中PS命令简介
1. 概述
有时,我们想关闭无响应的程序或检查我们的 Linux 系统中是否启动了后台进程。
在本快速教程中,我们将探索ps 进程监控命令如何在这种情况下帮助我们。
2. 语法
让我们看一下基本语法:
ps [options]
由于所有参数都是可选的,让我们看看它在没有选项的情况下做了什么:
$ ps
PID TTY TIME CMD
14900 pts/1 00:00:00 bash
14925 pts/1 00:00:00 ps
默认情况下,它会在四列中打印当前用户和终端的进程:
- *PID——*进程号
- *TTY——*与进程关联的终端
- TIME – 进程占用的 CPU 使用时间
- *CMD——*可执行命令
此外,与top 命令不同的是,我们只能在给定时间看到此信息的快照。
由于历史原因, ps接受各种格式的选项:
- 前面没有破折号(BSD 风格)
- 以破折号开头(UNIX 风格)
- 前面有两个破折号(GNU 风格)
虽然大多数选项在所有三种格式中都有等效项,但在本文的其余部分,我们将主要关注 UNIX 风格的语法。
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 – 进程所有者的用户标识
- 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. 列出线程
在某些情况下,了解特定进程的衍生线程可能很有用:
$ 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
让我们来看看发生了什么。我们首先启动gedit 文本编辑器并在其中键入一些随机字符串。
然后,我们按进程名称过滤并传递*-L*选项以打印出生成的线程。
附加的LWP列表示线程标识。
如果我们想要额外的输出,我们也可以将其与*-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 )**进行过滤。要获得它,我们需要修改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——**也称为会话领导者。在这种情况下,会话领导者是gnome-shell进程。
会话负责人生成了几个子进程,包括我们感兴趣的进程。我们现在可以在树视图输出中看到它们。
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命令支持多种输出修饰符。对于它们的完整描述,我们总是可以查阅手册页 。
由于有很多修饰符,让我们尝试一些更有趣的修饰符:
$ 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表示以千字节为单位的虚拟内存大小
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
真实用户不同于有效用户。那是因为passwd为root所有,但已被我们的user调用。
此特定行为对setuid 兼容的可执行文件有效。**当当前用户需要以临时提升的权限运行程序时,**就会发生这种情况。
ps命令可以使用*-U*选项按真实用户名进行过滤,使用-u*选项按有效用户名进行过滤。*