尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Linux下查看线程的实用方法:从ps、top到pidstat全掌握
排查 Linux 线程问题的时候最怕的不是不会用命令而是用错了命令看错了对象。很多刚接触 Linux 的同学一上来就问“怎么查看线程”但真正到了现场往往连线程和进程的关系都还没理清楚——拿ps一顿输出看到的全是进程 PID压根儿没看到线程的影子白白浪费半天时间。这篇文章就专门解决这个问题把 Linux 下查线程的常用手段、适用场景和坑位一次讲透适合刚入门的新手也适合排查问题时需要快速回忆命令的老手。1. 线程与进程先把这个概念掰清楚1.1 Linux 线程到底是怎么回事在 Windows 上线程和进程是两种非常明确的内核对象打开任务管理器你既能看进程也能看线程。但 Linux 不一样它对线程的实现方式很“取巧”——线程其实也是一种进程只不过它和父进程共享了大部分资源内存地址空间、文件描述符、信号处理等内核里管这种共享资源的进程叫“轻量级进程”LWPLightweight Weight Process。这个设计带来的直接后果就是你平时用ps -ef查看系统状态时默认看到的只是进程不会单独列出线程。但这并不代表线程不存在而是 Linux 把线程藏在了进程底下。要看到线程就必须显式告诉命令“把每个线程也给我展示出来”这就是-T、-L、-H这些参数的由来。理解这一点非常重要因为后面所有查看线程的命令本质上都是在做同一件事把进程内部那些 LWP 一个个单独列出来并标注上线程 IDTIDThread ID。1.2 查看线程前的心态调整排查线程问题其实就是排查一种特殊的进程问题。线程占 CPU、线程卡死、线程数过多、线程栈异常——这些场景看起来各不相同但底层都得靠一套命令去定位。所以我不建议你死记硬背一堆命令而是建立一条清晰的排查链路先确认进程 PID → 再列出这个进程下的所有线程 → 观察线程状态和 CPU 占用 → 定位异常线程 ID → 分析这个线程的调用栈。有了这条链路不管工具怎么换思路都不会乱。下面我会按照这条链路把每步对应的命令和实际使用场景拆开讲。2. 用 ps 命令快速查看线程2.1 -T 参数三秒快速上手如果你只想用最少的参数快速看到某个进程下面的线程ps -T是最直接的。比如我想看 PID 为 12345 这个进程下的线程ps -T -p 12345输出里会多出一个 SPID 列这个 SPID 就是线程的 ID。除了线程 ID你还能看到这个线程的 CPU 时间、状态等基本信息。这个命令的好处是简单适合在没太多时间思考的时候先打个底看看进程内部到底开了多少线程。但要注意一点ps -T的输出默认不会做任何排序多个线程的排列顺序比较随意想找最耗 CPU 的线程你得配合--sort-pcpu这类参数排序。2.2 -L 参数带 LWP 信息的进阶版ps -L是另一个常用参数它和-T的区别主要在于输出列名。用ps -L -p 12345查看时输出里会有两列非常重要的信息LWP 和 NLWP。LWP 就是前面说的轻量级进程 ID也就是线程 IDNLWP 表示这个进程下面总共开了多少个线程。ps -L -p 12345 # 输出示例 PID LWP TTY TIME CMD 12345 12345 ? 00:00:01 main 12345 12346 ? 00:00:00 worker-1 12345 12347 ? 00:00:00 worker-2这个命令非常适合在检查“线程数是否符合预期”的时候用。比如某个服务的配置里写了最大线程池大小是 50结果你用ps -L一看 LWP 有几百个那很明显有问题要么代码里的线程池没有回收要么中间件内部偷偷起了额外线程。如果只想统计线程数量直接用wc -l管道处理即可ps -L -p 12345 | wc -l注意输出里包含标题行实际线程数要减 1。当然也可以直接读/proc获取精确数值后面会讲。2.3 自定义输出字段把你想看的信息全部列出来默认输出虽然够用但排查性能问题时远远不够。我更推荐自定义输出列这样可以把 PID、TID、CPU 占用率、内存、状态、命令全部放在同一行一眼就能定位问题ps -eLo pid,lwp,stat,pcpu,pmem,time,comm --sort-pcpu这条命令会把系统里所有线程按 CPU 占用率从高到低排列瞬间就能看到哪个线程在疯狂刷 CPU。如果你已经锁定了进程 PID可以加上-p缩小范围ps -eLo pid,lwp,stat,pcpu,pmem,time,comm -p 12345 --sort-pcpu有时候你会看到comm列显示的不是线程名而是进程名这跟线程是否有独立的名称有关。真正规范的线程会通过pthread_setname_np()设置名字比如 Java 线程会显示成pool-1-thread-2这种可读性很强的名字。用ps查看线程还有一个实用场景判断多线程程序是否真的在工作。有些网络服务主进程看起来一直在运行但工作线程因为某种原因全部阻塞了这时候你会看到很多线程状态为S可中断睡眠而没有一个R运行中这往往意味着请求堆积但处理能力已经停滞。3. 用 top/htop 实时监控线程3.1 top -H 与 H 键切换ps是快照式查看也就是某一瞬间的状态。但线程问题经常是动态的比如某个线程每隔一段时间就飙高一次 CPU这时候就得靠 top 这类动态监控工具。查看实时线程最常用的命令是top -H -p 12345-H参数表示按线程模式显示-p指定进程 PID。进入 top 界面后按大写H可以在线程视图和进程视图之间切换。这一点非常实用因为有时你只是照例看 CPU突然发现整个进程占用很高但分不清是哪个线程在捣鬼就可以在 top 里按H展开看到具体的 LWP。top 里看到的每行都代表一个线程PID 列显示的其实是 TIDS列是线程状态%CPU是该线程消耗的 CPU。如果top -p PID之后发现整个进程 CPU 高立刻按H展开通常就能定位到那几个疯跑的线程。3.2 top 内排序与字段定制top 进去之后默认按 CPU 占用排序如果想按内存、时间等排序按O可以设置排序字段按f可以增删显示的列。对于线程排查我一般关注这几个列PID线程ID、%CPU、TIME、S、COMMAND。很多时候你会发现一个进程总 CPU 占用 300%但有几十个线程每个只占 2%~5%这说明负载比较均匀地分散在线程池里属于正常情况。而如果总 CPU 占用 300%其中某一个线程占 290%那基本可以确定代码里存在线程内死循环或者单点热点逻辑。3.3 htop 的树形视图更直观除了 tophtop在查看线程方面也很顺手。如果系统里没装也没关系多数发行版一条命令就能装好。htop 默认就会展示线程只是线程默认是折叠在进程下面的。按t键可以切换到树形视图父子关系一目了然按H键可以切换是否隐藏用户线程。htop 里还有一个好处是可以直接用鼠标点击选中某个线程按下F5把这个线程下的调用关系展开成树。虽然这个操作对定位线程 ID 没有决定性帮助但视觉上比 top 舒服很多适合长时间挂在终端观察。4. 用 pidstat 按线程统计 CPU 情况4.1 基础用法pidstat 是 sysstat 包里的工具如果你没装过 sysstat可能找不到这个命令。它的专业之处在于按时间间隔采样比如每秒输出一次各线程的 CPU 使用率pidstat -t -p 12345 1 5-t表示显示线程级别的统计信息1 5表示每秒采样一次共采样 5 次。输出里会有 TID 列和 %CPU 列比 top 更适合写进脚本做性能记录。如果不想限制 PID可以不带-p让它输出整个系统所有线程的统计pidstat -t 1这在对比不同进程线程负载时会非常方便。4.2 定位某个线程的 CPU 飙高问题我排查过一个典型问题某服务进程 CPU 持续飙高但top加载进界面后因为线程数量太多排序混乱很难看清。这时候我直接用pidstat -t -p PID 1持续采样日志里每个线程的 CPU 一目了然很快就抓到某个 TID 稳定占用 80% 以上。拿到 TID 之后下一步就是看这个线程到底在干什么。可以用cat /proc/PID/task/TID/stack查看内核栈或者用gdb附加进程打印用户态调用栈。再不行还可以用perf top -t TID做性能采样直接看到函数级别的热点。链路是这样的先找到 TID再深入分析而不是一上来就瞎猜。4.3 附加内存与上下文切换信息pidstat 还能进一步展开pidstat -t -r -p 12345 1-r可以查看每线程的内存缺页统计-w可以查看上下文切换次数。上下文切换次数异常升高往往意味着线程数过多、锁竞争激烈或者线程在频繁让出 CPU。我用这个字段判断线程池是不是设置得过大比单纯看线程数更可靠——因为有些线程数很多但排布合理切换并不频繁而有些线程数不多却因为频繁抢锁导致上下文切换暴涨。5. 用 /proc 文件系统查看线程细节5.1 /proc/PID/task 目录结构Linux 的/proc是一个虚拟文件系统直接映射内核里的进程和线程结构所以查看线程最底层的办法就是读/proc目录。一个 PID 对应的所有线程都挂在/proc/PID/task/下面。每有一个线程这个目录里就有一个以 TID 命名的子目录。比如ls /proc/12345/task/输出里那些数字就是线程 ID。想统计线程数量ls /proc/12345/task/ | wc -l这个方法返回的就是精确的线程数不像ps那样还要减标题行。而且/proc不受工具版本影响无论在脚本里还是容器里都能稳定工作。5.2 从 /proc 里读线程状态和栈每个 TID 目录下面还有很多文件其中最常用的是cat /proc/12345/task/12346/status这个命令能看到线程的状态State、父子关系、所属进程 PID 等信息非常全。如果线程卡在某个系统调用里可以用cat /proc/12345/task/12346/wchan cat /proc/12345/task/12346/syscallwchan表示线程正在等待的内核函数名syscall表示当前正在执行的系统调用号。这俩字段对定位线程卡死特别有用比如线程大量阻塞在futex_wait通常意味着它们在等待锁阻塞在poll或epoll_wait说明它们在等待网络事件。/proc还能直接读线程的/proc/PID/task/TID/stack但注意这个文件通常需要 root 权限而且某些内核版本可能限制访问。读到的内核栈信息可以帮助判断线程是否在内核态长时间旋转比如自旋锁导致的 CPU 飙升。5.3 进程级 status 里的 Threads 字段除了看/proc/PID/task/目录另一个快速判断线程数量的方式是直接看进程的 status 文件cat /proc/12345/status | grep Threads例如输出Threads: 12这个数值代表进程当前的线程数量跟ls /proc/12345/task | wc -l结果一致。在一段性能排查中我会每隔几秒采集一次这个数据看线程数是否在持续增长。如果线程数单调上升、不回落基本上就是线程泄漏了——大量线程创建后没有退出。这比 CPU 占用更能提前暴露问题因为线程泄漏初期 CPU 可能并不高但资源最终会被耗尽。6. 调试场景下的线程视图gdb / pstack / jstack6.1 gdb 附加进程查看线程当你需要查看某个线程正在执行什么函数静态视图就不够用了必须动态附加进程。gdb是排查 C/C 多线程程序的利器。先附加进程gdb -p 12345进入 gdb 后info threads这会列出进程下的所有线程包括每个 LWP 的 ID 和当前执行位置。切换线程thread 2然后打印调用栈bt如果要把所有线程的调用栈全部打出来可以一次执行thread apply all bt输出很长建议把 gdb 输出重定向到文件里再慢慢分析。gdb 附加进程会让程序暂停一段时间生产环境使用要谨慎最好在流量低峰操作或者使用gcore先导出 core 文件再离线分析。6.2 pstack 快速打印线程栈如果不想进 gdb 那么“重”pstack是一个更轻量的选择。直接对着 PID 执行pstack 12345它会打出这个进程所有线程的用户态调用栈不需要交互。速度比 gdb 快适合抓现场。缺点是某些发行版默认不装需要额外安装而且面对极度复杂的程序时输出可读性不如 gdb。pstack 在 Java 服务上也能用但输出的是 C 虚拟机层面的栈对 Java 代码逻辑帮助有限。做 Java 排查的话还是得靠 jstack。6.3 Java 服务的 jstackJava 的多线程模型和系统线程的对应关系比较绕一个 Java 线程对应一个 JVM 内部的原生线程原生线程又对应一个 Linux LWP。如果你想看 Java 层面某个线程在干什么用jstack PID会直接打印出 Java 线程名、线程状态、栈帧以及对应的 nidnative thread ID 的十六进制形式。这个 nid 可以用来和系统线程 ID 对上号。比如系统层用top -H看到一个 TID 是 31492转成十六进制是 0x7B04到 jstack 输出里搜nid0x7b04就能定位到具体的 Java 线程名和业务代码位置。Java 线程名往往能直接说明问题比如http-nio-8080-exec-11表示这是处理 HTTP 请求的工作线程如果大量线程卡在java.lang.Thread.State: WAITING (parking)且堆内大量对象堆积那基本可以判断是线程池满、队列拥堵。6.4 strace 跟踪线程系统调用还有一种动态场景是跟踪线程的系统调用行为strace加-f参数会跟随线程strace -f -p 12345这样每个线程的系统调用都会输出带上 TID 前缀。这个工具适合排查线程是否反复打开文件、频繁 socket 连接、或者陷入奇怪的系统调用循环。缺点也是会拖慢目标进程生产环境慎用。7. 常见问题与排查技巧实录7.1 为什么实际线程数比代码里创建的还多这个坑几乎人人踩过。用ps -L或/proc一数线程发现线程数远超业务代码里手动创建的线程池大小。原因主要有几个运行时自带后台线程。JVM 至少有 GC 线程、编译器线程、信号处理线程等Go 运行时也有自己的后台调度线程。第三方库偷偷起线程。比如某些日志库、监控 SDK、连接池组件都会在内部创建线程而且名字常常不显眼。线程创建了但没回收。这属于资源泄漏排查时最危险因为线程数会越涨越多。遇到线程数超预期不要急着怪代码逻辑先统计一段时间内线程数是否稳定。稳定就意味着大概率是运行时正常开销持续增长才是问题。7.2 线程状态怎么解读查看线程时STAT列经常出现字母组合常见的有R正在运行或正在等待运行。S可中断睡眠通常等待某个事件比如 IO 完成或定时器。D不可中断睡眠一般是内核态 IO 阻塞比如读取磁盘。看到大量D状态线程要注意是否磁盘出问题。T被停止比如被SIGSTOP暂停。Z僵尸状态线程已结束但没被回收。在正常多线程程序里僵尸线程不常见一旦出现要重点检查线程退出后的资源回收逻辑。ps -L输出里如果大量线程是S而业务请求没有响应大概率线程池在空转或等待锁这时候要去抓线程栈看看它们到底在哪里 sleep。7.3 线程暴涨怎么快速定位线程像雪崩一样暴涨时首要任务是找出创建线程的源头而不是逐行读日志。我通常按两步走先看整体趋势确认线程数在涨for i in $(seq 1 10); do cat /proc/PID/status | grep Threads; sleep 1; done再在短时间内抓几次线程列表对比新出现的 TID 规律ps -eLo pid,lwp,comm -p PID | sort -k2 -n | tail -20如果新线程名都带同一前缀比如worker-thread-、pool-1-thread-那八成是某个线程池在不断创建新线程执行任务。此时把高并发场景下的任务入口代码翻出来检查十有八九是任务队列积压、线程池参数设置不当或者业务逻辑中每个请求都会创建新的临时线程。还有一种隐蔽情况是每次请求都触发创建新线程但线程执行完又无法退出最后堆积成山。这种往往不是线程池问题而是原始线程创建方式没有配合join或者通过executor管理导致大量游离线程。7.4 常见的“查不到线程”情形排查环境是容器时/proc可能只暴露容器内可见的信息某些工具依赖的权限会被限制导致查看/proc/PID/task时提示权限不足。这时优先确认是否能进入目标进程命名空间以及是否具备SYS_PTRACE权限否则gdb、strace这类动态附加工具基本用不了。另外如果进程 PID 是复用过的你在查看时小心旧数据残留。每次都先ps -ef | grep 进程名确认 PID 再继续避免盯着错误目标查半天。7.5 高效输出技巧给现场留证据排查线程问题时现场信息转瞬即逝一定要第一时间留存。我自己常用的命令组合ps -eLo pid,tid,stat,pcpu,comm --sort-pcpu thread_snapshot.txt pidstat -t -p PID 1 10 pidstat.log pstack PID pstack.log 21这些输出可以组合成一份排查记录方便对比 CPU、线程栈在不同时间的变化。比一次次手抄和截图靠谱得多。8. 我常用的几条命令速查整理一份自己常用的速查表排查时效率会高很多排查需求推荐命令查看进程下所有线程ps -T -p PID查看线程 ID 与线程数ps -L -p PID按 CPU 排序看系统所有线程ps -eLo pid,lwp,stat,pcpu,comm --sort-pcpu实时看某进程的线程 CPUtop -H -p PID按线程周期采样 CPUpidstat -t -p PID 1精确线程数ls /proc/PID/task/ | wc -l查看线程状态和栈信息cat /proc/PID/task/TID/status快速打印线程栈pstack PID动态调试线程gdb -p PID后执行info threads查看 Java 线程栈jstack PID老实说工具再多最常用的也就前五条。把ps -L、top -H、pidstat -t这三件套练熟绝大多数线程问题已经能定位到 TID 级别剩下那些一两百字的异常栈才值得动用gdb和pstack深入。我自己的习惯是先在/proc目录确认线程数量和状态的轮廓再决定要不要上重型工具因为这一步成本最低信息也最直接。排查线程问题跟排查进程问题没有本质差别关键是别把线程当作“另一种神秘实体”它就是一组共享资源的进程找到它读懂它的状态答案自然就出来了。
RELATED

相关推荐

LocalAI本地AI推理实战:部署LLM、语音与文生图全攻略

LocalAI本地AI推理实战:部署LLM、语音与文生图全攻略

之前做本地 AI 推理时,最头疼的问题就是“换一个模型就要换一套环境”,有时候还要为 GPU、CUDA、Python 版本折腾一整天。后来接触到 LocalAI,发现它把这些碎片化的痛点统一收敛到了一个服务里:不管是跑大语言模型、语音识别、文字…

📅 2026/10/12 2:42:34
本地跑AI模型新标准:LocalAI 实战部署与OpenAI兼容API接入

本地跑AI模型新标准:LocalAI 实战部署与OpenAI兼容API接入

这次我们来看一个把“本地跑 AI 模型”这件事做成统一标准的开源项目:LocalAI。简单说,LocalAI 是一个免费、开源的本地推理服务框架,目标是让你在普通电脑甚至树莓派这类低功耗硬件上,直接运行大语言模型、语音识别、语音合成、图…

📅 2026/10/12 2:42:34
C# 斑马打印机打印 Demo 完整版:RAW 直发与驱动打印实战

C# 斑马打印机打印 Demo 完整版:RAW 直发与驱动打印实战

简介:这份资源是面向C#开发者的Zebra打印机集成示例项目,适合需要在物流、零售、医疗等场景中实现标签、收据及条形码打印的初中级开发者参考。压缩包共88个文件,约348KB,以cs源码、sample示例、resx资源、config配置、csproj工程…

📅 2026/10/12 2:42:34
MORE NEWS

更多资讯

📰

声呐阵列信号处理——声呐阵列波束形成(第一章第三节)

一、声呐阵列模型3.接收数据模型(1)数据组成阵元的实际接收数据是信号、噪声等干扰的叠加,所以接收数据模型建立的前提需是信号模型、噪声模型的构建。对于第m个阵元,其接收数据可以表示为数据中包含期望信号,D个干扰信…

📰

深入 Freelens 扩展契约测试桩:`@freelensapp/fixture-extension` 如何让“静默破坏“无处遁形

云原生开发工具运维 【免费下载链接】freelens Free IDE for Kubernetes 项目地址: https://gitcode.com/gh_mirrors/fr/freelens 点击查看 免费下载 导读 Freelens(Kubernetes 免费 IDE)通过 freelensapp/extensions 向第三方暴露扩展契约…

📰

ant-design-blazor TreeSelect 弹出位置(placement)完全指南:手动指定下拉弹出方向与底层实现解析

前端UI组件设计系统 【免费下载链接】ant-design-blazor 基于 Ant Design 与 Blazor 的前端组件库。让开发者解放生产力,实现更大价值。 项目地址: https://gitcode.com/ant-design-blazor/ant-design-blazor 点击查看 免费下载 placement 是 ant-desig…

📰

使用 Jaeger Go 客户端(jaeger-client-go)为 Go 服务接入 OpenTracing 分布式追踪

云原生可观测性容器编排运维 【免费下载链接】scope Monitoring, visualisation & management for Docker & Kubernetes 项目地址: https://gitcode.com/gh_mirrors/sc/scope 点击查看 免费下载 jaeger-client-go 是 Uber 提供的 Jaeger 官方 Go 探针库&am…

📰

Infosec_Reference 之 ICS/SCADA 安全资源指南:从协议原理到攻防工具链

网络安全教程 【免费下载链接】Infosec_Reference An Information Security Reference That Doesnt Suck; https://rmusser.net/git/admin-2/Infosec_Reference for non-MS Git hosted version. 项目地址: https://gitcode.com/gh_mirrors/in/Infosec_Reference 点击…

📰

基于微信小程序与SSM的小区管理系统开发实践

1. 项目概述与选题背景第一次看到“基于微信小程序的小区管理系统”这个题目,很多人的第一反应是:这不就是一个普通的CRUD项目吗?其实真做下来你会发现,这个项目的难度不在代码量,而在“业务流程的闭环”和“多端数据的…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬