Contents

为什么htop显示比ps更多的进程

1. 概述

作为 Linux 用户,我们经常希望确定系统上正在运行的进程的总数。此外,有时我们可能想要调查特定程序或命令在做什么。为此,我们可以依赖不同的工具,例如htop 和*ps *。

在本教程中,我们将在监控进程和线程时检查这些命令的输出。

2. 如何安装htop

htop是一个交互式工具,可以让我们实时查看和管理正在运行的进程和线程。它也是一个非常有用的命令来监控系统资源,例如 CPU 和内存使用情况。

让我们看看我们如何分别在 Ubuntu/Debian 和 CentOS 上安装这个实用程序。

对于 Ubuntu/Debian:

$ sudo apt install htop

对于 CentOS:

$ yum install epel-release
$ yum install htop
$ htop -v
htop 2.2.0 - (C) 2004-2019 Hisham Muhammad
Released under the GNU GPL.

在接下来的部分中,我们将使用htop的 2.2.0 版本。

3. 比较htopps的输出

htop和 ps都是 进程管理器。让我们看看它们默认为特定进程显示的内容。

3.1. 使用htop检查mysqld进程

使用htop,我们可以从命令行将输出限制为特定的进程 ID。为了说明手头的问题,让我们将 MariaDB 服务器进程 ID 作为值传递给–pid选项:

$ htop --pid=$(pidof mysqld)
1  [||                                                             2.6%]   Tasks: 51, 43 thr; 2 running
2  [                                                               0.0%]   Load average: 0.00 0.02 0.05 Mem[|||||||||||||||||||||||||||||||||||||||||||||             397M/991M]   Uptime: 04:45:44
Swp[                                                           0K/2.00G]
PID USER      PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
17173 mysql      20   0  948M 88316  7108 S  0.7  8.7  0:00.73 /usr/libexec/mysqld --basedir=/usr --datadir=/data/lib/mysql --plugin-dir=/usr/lib64/mysql/
17184 mysql      20   0  948M 88316  7108 S  0.0  8.7  0:00.03 /usr/libexec/mysqld --basedir=/usr --datadir=/data/lib/mysql --plugin-dir=/usr/lib64/mysql/
17177 mysql      20   0  948M 88316  7108 S  0.0  8.7  0:00.03 /usr/libexec/mysqld --basedir=/usr --datadir=/data/lib/mysql --plugin-dir=/usr/lib64/mysql/
...
17193 mysql      20   0  948M 88316  7108 S  0.0  8.7  0:00.00 /usr/libexec/mysqld --basedir=/usr --datadir=/data/lib/mysql --plugin-dir=/usr/lib64/mysql/
17202 mysql      20   0  948M 88316  7108 S  0.0  8.7  0:00.00 /usr/libexec/mysqld --basedir=/usr --datadir=/data/lib/mysql --plugin-dir=/usr/lib64/mysql/
17203 mysql      20   0  948M 88316  7108 S  0.0  8.7  0:00.00 /usr/libexec/mysqld --basedir=/usr --datadir=/data/lib/mysql --plugin-dir=/usr/lib64/mysql/
F1Help  F2Setup F3Search F4Filter F5Tree F6SortByF7Nice -F8Nice +F9Kill  F10Quit

在这里,我们使用*pidof 命令来查找正在运行的 MariaDB 程序名称的PID*。然后,使用命令替换 ,我们将pidof mysqld命令替换为其实际输出。

3.2. 使用ps列出mysqld进程

另一方面,让我们结合grep 命令和ps来根据mysqld 进程 ID 过滤输出:

$ ps -e -o pid,user,command | grep $(pidof mysqld) | grep -v grep
17173 mysql    /usr/libexec/mysqld --basedir=/usr --datadir=/data/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --log-error=/data/lib/mysql/log/mariadb.log --pid-file=/data/lib/mysql/run/mariadb.pid --socket=/data/lib/mysql/mysql.sock

在上面,我们将*-e选项传递给ps以选择所有进程,并将-o选项传递给仅打印某些字段。此外,我们使用带有-v选项的grep命令从最终输出中排除grep*本身。

我们注意到htopps输出相比显示了更多的进程。在下一节中,我们将讨论我们刚刚获得的输出。

4. 为什么进程数不同?

事实证明,htopps都从/proc 文件系统中读取了有关正在运行的进程和线程数的详细信息。更具体地说,是*/proc/pid目录和/proc/pid/task/tid*的子目录。

让我们列出并计算*/proc/pid/task/下的子目录,将pid替换为正在运行的 MariaDB 服务器PID*:

$ ls /proc/$(pidof mysqld)/task
17173  17177  17178  17179  17180  17181  17182  17183  17184  17185  17186  17188  17189  17190  17191  17192  17193  17202  17203
$ ls -1 /proc/$(pidof mysqld)/task | wc -l
19

我们从上面的输出推断,ps不同,htop命令默认显示正在运行的进程及其对应的各个线程

htop旨在提供尽可能多的有关系统中正在发生的事情的信息。结果,它显示用户线程而不是隐藏它们。例如,系统管理员可以在多线程应用程序中发现问题,同时监控其相关线程的 CPU 使用率。然而,一些 Linux 用户更喜欢禁用此设置,因为他们发现在htop输出中显示所有线程不太有用。

5. 使用htop 的线程

在 Linux 中,我们有用户空间和内核空间线程 。用户线程与用户应用程序相关联,内核线程由操作系统创建和管理。

** htop默认显示用户线程并隐藏内核线程**。接下来,让我们检查一下如何修改此行为。

5.1. 用户线程

我们可以自定义htop配置来禁用用户线程的可见性。为了证明这一点,我们执行htop –pid=$(pidof mysqld),然后:

  1. 转到设置
  2. 显示选项
  3. 检查隐藏用户态进程线程
  4. F10

/uploads/htop_ps_more_processes/1.png

同样,我们可以简单地按H隐藏用户线程。

在上述更改之后,我们将输出限制为仅主 MariaDB 进程:

PPID   PID USER      PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
17007 17173 mysql      20   0  948M 88316  7108 S  0.7  8.7  0:01.07 /usr/libexec/mysqld --basedir=/usr --datadir=/data/lib/mysql --plugin-dir=/usr/lib64/

现在我们看到了ps向我们展示的相同过程。此外,让我们探讨两个流程管理器之间的另一个比较点。

5.2. 内核线程

从版本 2.2.0 开始,** htop通过检查*/proc/pid/cmdline是否为空来识别内核线程**。但是,最新的htop版本会根据/proc/pid/stat中的PF_KTHREAD*位值检测内核线程。

我们可以列出默认禁用的内核线程。首先,让我们启动htop然后添加一个新列:

  1. 转到设置
  2. 可用列
  3. 选择PPID

接下来,让我们点击K

PPID   PID USER      PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
    2 20265 root       20   0     0     0     0 S  0.7  0.0  0:00.08 kworker/1:1
17007 17173 mysql      20   0  948M 88316  7108 S  0.0  8.7  0:01.17 /usr/libexec/mysqld --basedir=/usr --datadir=/data/lib/mysql --plugin-dir=/usr/lib64/
17007 17182 mysql      20   0  948M 88316  7108 S  0.0  8.7  0:00.06 /usr/libexec/mysqld --basedir=/usr --datadir=/data/lib/mysql --plugin-dir=/usr/lib64/
...
    2    14 root       20   0     0     0     0 S  0.0  0.0  0:00.18 ksoftirqd/1
    2    16 root        0 -20     0     0     0 S  0.0  0.0  0:00.00 kworker/1:0H
    2    18 root       20   0     0     0     0 S  0.0  0.0  0:00.00 kdevtmpfs

从上面我们也看到了内核线程。例如,kworker/0:0HPPID =2 (Kthreadd作为父进程)。

此外, htop输出的右上角显示了一些关于总运行进程的有用信息:

Tasks: 53, 43 thr, 78 kthr; 2 running

这意味着根据htop,我们总共有 53 个进程、43 个用户线程、78 个内核线程和 2 个处于运行状态的任务。让我们将其与 ps进行比较。

6. 如何用ps列出线程

我们可以修改默认的ps输出以显示有关进程和线程的详细信息。要选择所有进程并显示线程,我们可以将*-eLf标志传递给ps*命令。

作为说明,让我们将输出限制为前 5 个mysqld线程,同时仍保留标头名称:

$ ps -eLf | head -n 1 ; ps -eLf | grep $(pidof mysqld) | head -n 5
UID PID PPID LWP C NLWP STIME TTY TIME CMD
mysql 17173 17007 17173 0 19 13:30 ? 00:00:00 /usr/libexec/mysqld --basedir=/usr --datadir=/data/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --log-error=/data/lib/mysql/log/mariadb.log --pid-file=/data/lib/mysql/run/mariadb.pid --socket=/data/lib/mysql/mysql.sock
mysql 17173 17007 17177 0 19 13:30 ? 00:00:00 /usr/libexec/mysqld --basedir=/usr --datadir=/data/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --log-error=/data/lib/mysql/log/mariadb.log --pid-file=/data/lib/mysql/run/mariadb.pid --socket=/data/lib/mysql/mysql.sock
mysql 17173 17007 17178 0 19 13:30 ? 00:00:00 /usr/libexec/mysqld --basedir=/usr --datadir=/data/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --log-error=/data/lib/mysql/log/mariadb.log --pid-file=/data/lib/mysql/run/mariadb.pid --socket=/data/lib/mysql/mysql.sock
mysql 17173 17007 17179 0 19 13:30 ? 00:00:00 /usr/libexec/mysqld --basedir=/usr --datadir=/data/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --log-error=/data/lib/mysql/log/mariadb.log --pid-file=/data/lib/mysql/run/mariadb.pid --socket=/data/lib/mysql/mysql.sock
mysql 17173 17007 17180 0 19 13:30 ? 00:00:00 /usr/libexec/mysqld --basedir=/usr --datadir=/data/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --log-error=/data/lib/mysql/log/mariadb.log --pid-file=/data/lib/mysql/run/mariadb.pid --socket=/data/lib/mysql/mysql.sock

在上面的示例中,我们结合了*-Lf选项来添加LWP*(线程 ID)和NLWP(线程数)列。NLWP列显示我们有 19mysqld线程

最后,我们可以通过传递*–ppid* 和 -p选项来查看和统计内核线程的数量,分别根据父进程 ID 和进程 ID 进行选择:

$ ps  --ppid 2 -p 2 --format pid,ppid,time,cmd
  PID  PPID     TIME CMD
    2     0 00:00:00 [kthreadd]
    4     2 00:00:00 [kworker/0:0H]
    6     2 00:00:00 [ksoftirqd/0]
    7     2 00:00:00 [migration/0]
    8     2 00:00:00 [rcu_bh]
    9     2 00:00:01 [rcu_sched]
   10     2 00:00:00 [lru-add-drain]
   11     2 00:00:00 [watchdog/0]
   ...
  6570     2 S 00:00:00 [kworker/1:0]
  25359    2 S 00:00:00 [kworker/0:0]
 25761     2 S 00:00:00 [kworker/1:2]
 26957     2 S 00:00:00 [kworker/0:2]

在这种情况下,我们使用*–format*标志来指定列。

从上面的输出中,我们列出了内核线程kthreadd ( PID =2) 及其所有子线程 ( PPID =2)

最后,让我们像使用htop一样计算内核线程的总数:

$ ps --ppid 2 -p 2 --format pid,ppid,state,time,cmd | tail -n +2 | wc -l
78

我们使用tail 命令跳过计算第一列标题。

如上所示,使用 ps 命令显示的内核线程数与使用 htop 显示的 kthr 数相同。