Contents

在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

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

此特定行为对setuid 兼容的可执行文件有效。**当当前用户需要以临时提升的权限运行程序时,**就会发生这种情况。

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