1. 背景
现在的 CPU 基本都是由多个核心组成,加上我们现在的微服务,更加充分利用多核处理器的强大功能,我们的微服务都会开启多线程进行各种任务处理,这也导致我们遇到问题的时候很难定位。例如我们线上排查问题,可能需要有关特定进程下使用多少线程的信息
,但如果没有一些强大的APM
工具,我发现身边一些同事其实还不太了解如何进行线程监控。在本文中,我们将向您介绍可用于检查和监控线程计数数据的所有工具和命令。
2. 查看线程数
接下来会演示下,在Linux服务器查看线程的几种方式。
2.1 使用ps
命令
有时候我们在线上定位问题,想知道服务器上每个服务到底开启了多少条线程,从而定位哪个服务有滥用线程。指令 ps
显示有关计算机上当前活动进程的信息。组合选项-e和-f是一种标准方法,我们可以使用它以格式化的方式获取有关每个进程的信息。同样,我们可以附加-L
选项让ps
报告额外的线程信息:
Last login: Mon Mar 20 14:45:58 2023 from 172.17.0.1
[root@ca51ce5b1371 ~]# ps -eLf
UID PID PPID LWP C NLWP STIME TTY TIME CMD
root 1 0 1 0 1 14:45 ? 00:00:00 /usr/sbin/init
root 28 1 28 0 1 14:45 ? 00:00:00 /usr/lib/systemd/systemd-journald
root 29 1 29 0 1 14:45 ? 00:00:00 /usr/lib/systemd/systemd-udevd
dbus 78 1 78 0 1 14:45 ? 00:00:00 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
root 79 1 79 0 1 14:45 ? 00:00:00 /usr/sbin/sshd -D -oCiphers=aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes256-cbc,aes128-gcm@openssh.com,aes128-ctr,aes128-cbc -oMACs=hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.
root 85 79 85 0 1 14:45 ? 00:00:00 sshd: root [priv]
root 87 85 87 0 1 14:45 ? 00:00:00 sshd: root@pts/1
root 88 87 88 0 1 14:45 pts/1 00:00:00 -bash
root 95 87 95 0 1 14:45 ? 00:00:00 /usr/libexec/openssh/sftp-server
root 119 88 119 0 1 14:47 pts/1 00:00:00 top
root 120 79 120 0 1 14:49 ? 00:00:00 sshd: root [priv]
root 122 120 122 0 1 14:49 ? 00:00:00 sshd: root@pts/2
root 123 122 123 0 1 14:49 pts/2 00:00:00 -bash
root 126 122 126 0 1 14:49 ? 00:00:00 /usr/libexec/openssh/sftp-server
root 153 123 153 0 1 14:50 pts/2 00:00:00 ps -eLf
- 「PID」: 唯一进程标识
- 「PPID」: 父进程标识
- 「LWP」: 进程中的唯一线程标识
- 「NLWP」: (轻量级进程数)列指示附加到该特定进程的线程数
例如,当我们查看一个 NLWP
值大于 1
的进程时,我们可以看到它之后的相应行数具有相同的 PID
值。但是,如果我们不想接收每个进程的所有这些混乱信息,而我们只想搜索特定进程的线程数,我们可以使用带有 -o
选项的ps
:
[root@ca51ce5b1371 ~]# ps -o nlwp 126
NLWP
1
这里的nlwp
是我们上面获取线程数信息的列,126是一个进程的PID
。正如我们所见,这个进程同时运行了1
个线程。除此之外,我们还可以通过使用thcount
而不是nlwp
来获得相同的结果:
[root@ca51ce5b1371 ~]# ps -o thcount 126
THCNT
1
还可以通过以下组合命令查询服务器上的所有线程数:ps -eo nlwp | tail -n +2 | awk '{ num_threads += $1 } END { print num_threads }'
[root@ca51ce5b1371 ~]# ps -eo nlwp | tail -n +2 | awk '{ num_threads += $1 } END { print num_threads }'
17
这里服务器的总线程数是17
2.2 检查/proc
目录
以类似的方式,我们可以检查/proc
目录中的线程计数数据。/proc
是一个虚拟文件系统,其中包含有关进程的各种信息
[root@ca51ce5b1371 ~]# ls /proc/126/task/
126
正如我们在上面清楚看到的,PID 为 126
的进程包含1
个线程(因为我这里的进程只有一条线程,所以这里看到的线程ID和进程 ID一样)。事实上,这个结果与我们之前使用命令ps
得到的结果相符。此外,我们可以验证这些目录名称与LWP 列下的相应结果完全相同。
[root@ca51ce5b1371 ~]# cat /proc/126/status | grep Threads
Threads: 1
3 实时监控线程数
3.1
ps
命令与watch
结合
当我们使用watch
工具 结合上一节提到的命令,我们可以实时监控任意进程的线程状态。我们还可以使用-n
选项以秒为单位指定更新间隔。
[root@ca51ce5b1371 ~]# watch -n 1 ps -o thcount 126
Every 1.0s: ps -o thcount 126 ca51ce5b1371: Mon Mar 20 16:52:36 2023
THCNT
1
请注意,以上结果每秒刷新一次。watch
每秒运行命令 ps -o thcount 126
并将输出显示回控制台。
3.2 使用top
命令
top
是一个内置程序,可报告 CPU 使用率
和内存状态
等系统信息。我们可以使用此工具来获取每个进程的线程数信息,但默认情况下它是禁用的
。因此,我们必须手动调整该工具。首先,我们需要进入实用程序:
top - 16:54:58 up 2:10, 2 users, load average: 0.03, 0.03, 0.00
Tasks: 15 total, 1 running, 14 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.1 us, 0.2 sy, 0.0 ni, 99.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 7851.4 total, 6434.9 free, 329.5 used, 1087.0 buff/cache
MiB Swap: 1024.0 total, 1024.0 free, 0.0 used. 6983.8 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 21456 9212 7048 S 0.0 0.1 0:00.28 systemd
28 root 20 0 26528 7976 7104 S 0.0 0.1 0:00.04 systemd-journal
29 root 20 0 30576 7316 5684 S 0.0 0.1 0:00.05 systemd-udevd
78 dbus 20 0 10084 3364 2952 S 0.0 0.0 0:00.00 dbus-daemon
79 root 20 0 14724 6356 5484 S 0.0 0.1 0:00.00 sshd
85 root 20 0 19728 8792 7568 S 0.0 0.1 0:00.02 sshd
87 root 20 0 19728 5240 4008 S 0.0 0.1 0:01.38 sshd
88 root 20 0 6720 3292 2816 S 0.0 0.0 0:00.00 bash
95 root 20 0 7080 4192 3676 S 0.0 0.1 0:00.00 sftp-server
119 root 20 0 12068 3664 3100 S 0.0 0.0 0:05.92 top
120 root 20 0 19728 9008 7788 S 0.0 0.1 0:00.03 sshd
122 root 20 0 19728 5256 4036 S 0.0 0.1 0:00.12 sshd
123 root 20 0 6852 3224 2740 S 0.0 0.0 0:00.04 bash
126 root 20 0 7080 4292 3776 S 0.0 0.1 0:00.00 sftp-server
522 root 20 0 12068 3732 3168 R 0.0 0.0 0:00.00 top
默认情况下,我们是没办法看到线程数这一列,这也是很多初级开发人员误以为top
命令只显示这一些内容。实际上我们如果按下f
键盘,就可以进入top
的指标列表。按上下可以自由选择你要显示的指标,在这里按 d
可以选择nTH
字段去显示线程数指标
,然后按q
返回主界面:
Fields Management for window 1:Def, whose current sort field is %CPU
Navigate with Up/Dn, Right selects for move then or Left commits,
'd' or toggles display, 's' sets sort. Use 'q' or to end!
* PID = Process Id SUPGRPS = Supp Groups Names
* USER = Effective User Name TGID = Thread Group Id
* PR = Priority OOMa = OOMEM Adjustment
* NI = Nice Value OOMs = OOMEM Score current
* VIRT = Virtual Image (KiB) ENVIRON = Environment vars
* RES = Resident Size (KiB) vMj = Major Faults delta
* SHR = Shared Memory (KiB) vMn = Minor Faults delta
* S = Process Status USED = Res+Swap Size (KiB)
* %CPU = CPU Usage nsIPC = IPC namespace Inode
* %MEM = Memory Usage (RES) nsMNT = MNT namespace Inode
* TIME+ = CPU Time, hundredths nsNET = NET namespace Inode
* COMMAND = Command Name/Line nsPID = PID namespace Inode
PPID = Parent Process pid nsUSER = USER namespace Inode
UID = Effective User Id nsUTS = UTS namespace Inode
RUID = Real User Id LXC = LXC container name
RUSER = Real User Name RSan = RES Anonymous (KiB)
SUID = Saved User Id RSfd = RES File-based (KiB)
SUSER = Saved User Name RSlk = RES Locked (KiB)
GID = Group Id RSsh = RES Shared (KiB)
GROUP = Group Name CGNAME = Control Group name
PGRP = Process Group Id NU = Last Used NUMA node
TTY = Controlling Tty
TPGID = Tty Process Grp Id
SID = Session Id
* nTH = Number of Threads
P = Last Used Cpu (SMP)
TIME = CPU Time
SWAP = Swapped Size (KiB)
CODE = Code Size (KiB)
DATA = Data+Stack (KiB)
nMaj = Major Page Faults
nMin = Minor Page Faults
nDRT = Dirty Pages Count
WCHAN = Sleeping in Function
Flags = Task Flags
CGROUPS = Control Groups
SUPGIDS = Supp Groups IDs
选择之后的界面会多了nTH
这一列,这样我们就可以看到每个进程下到底开启了多少线程,有没有微服务程序滥用线程:
top - 17:00:41 up 2:15, 2 users, load average: 0.08, 0.03, 0.01
Tasks: 15 total, 1 running, 14 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.1 us, 0.1 sy, 0.0 ni, 99.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 7851.4 total, 6429.4 free, 335.0 used, 1087.0 buff/cache
MiB Swap: 1024.0 total, 1024.0 free, 0.0 used. 6978.4 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND nTH
1 root 20 0 21456 9212 7048 S 0.0 0.1 0:00.28 systemd 1
28 root 20 0 26528 7976 7104 S 0.0 0.1 0:00.04 systemd-journal 1
29 root 20 0 30576 7316 5684 S 0.0 0.1 0:00.05 systemd-udevd 1
78 dbus 20 0 10084 3364 2952 S 0.0 0.0 0:00.00 dbus-daemon 1
79 root 20 0 14724 6356 5484 S 0.0 0.1 0:00.00 sshd 1
85 root 20 0 19728 8792 7568 S 0.0 0.1 0:00.02 sshd 1
87 root 20 0 19728 5240 4008 S 0.0 0.1 0:01.45 sshd 1
88 root 20 0 6720 3292 2816 S 0.0 0.0 0:00.00 bash 1
95 root 20 0 7080 4192 3676 S 0.0 0.1 0:00.00 sftp-server 1
119 root 20 0 12068 3664 3100 S 0.0 0.0 0:06.19 top 1
120 root 20 0 19728 9008 7788 S 0.0 0.1 0:00.03 sshd 1
122 root 20 0 19728 5256 4036 S 0.0 0.1 0:00.16 sshd 1
123 root 20 0 6852 3224 2740 S 0.0 0.0 0:00.04 bash 1
126 root 20 0 7080 4292 3776 S 0.0 0.1 0:00.00 sftp-server 1
522 root 20 0 12068 3732 3168 R 0.0 0.0 0:00.14 top