
凌晨一点被叫起来登录服务器第一眼看到 top 的 CPU 已经红到顶业务进程不知道卡在哪端口的连接越积越多日志还在几百兆地往磁盘里灌。这种场景干运维的人和写后端的人多半都不陌生。真正让人加班的往往不是某个诡异的问题本身而是你坐在终端前脑子里没有一个清晰的排查链路。这个系列写到第六篇我不打算再铺基础命令了。前几篇把文件、权限、软件包、网络这些操作都过了一遍这一篇换个更实战的视角系统已经跑起来了出了故障怎么用一批 Linux 常用命令从进程、日志、磁盘、系统调用四个维度把问题钉死在现场。适合刚转运维的同学也适合开发同学在排查生产问题时对照着用。1. 先建立运行态诊断的思路而不是背命令清单1.1 把这篇文章当成一份“急诊手册”平时确认配置、装软件、看端口那是体检半夜被叫起来处理故障那是急诊。体检可以慢慢查急诊必须按优先级来。我自己养成的习惯是先保全现场再动手处理。很多人遇到问题第一反应是重启服务但重启会把现场全毁了——进程没了、内核日志被冲刷、临时文件被清理后面复盘只能靠猜。所以任何时候上服务器排查我脑子里先过一遍五件事服务本身通不通、进程还活着没、日志在说什么、磁盘和端口有没有异常、必要时再深入系统调用和线程栈。这条链路对应到命令上就是 systemctl / curl → ps / top → journalctl / tail → df / ss → strace / gdb。每一层如果很快能定位就直接收工定不了就顺着往下钻一层。1.2 本篇用到的命令总览先给一张“藏宝图”后面每章再展开讲细节。这篇不会把参数全部罗列只挑真正解决问题的部分。维度核心命令常见场景服务状态systemctl status、curl -I、ss -tlnp服务挂了、端口不通、HTTP 不响应进程状态top、htop、ps、pidof、pgrepCPU 跑满、内存泄漏、找不到进程日志检索journalctl、tail、grep、zgrep报错时间窗、异常堆栈、日志切分磁盘空间df -h、df -i、du、find磁盘满、inode 满、大文件定位文件与端口lsof端口被占、文件被删没释放系统调用strace、gdb进程卡住、读不到配置、崩溃栈网络状态ss、netstatTIME_WAIT 堆积、连接数异常1.3 先做几个“现场快照”再开始折腾正式排查前我推荐先花三十秒采集一批现场数据存到一个临时目录里。别小看这一步故障结束后做复盘和给同事交接时这些输出比聊天记录有用得多。mkdir -p /tmp/debug_$(date %Y%m%d_%H%M%S) cd /tmp/debug_$(date %Y%m%d_%H%M%S) date date.txt ps aux ps_aux.txt ss -an ss_an.txt df -h df_h.txt journalctl -k -n 200 --no-pager dmesg_tail.txt这几个命令本身都是 Linux 常用命令里的熟面孔组合起来就是故障现场的“四件套”。等排查结束再看这些快照里哪些数据对得上问题现象会很有收获。2. 进程篇看谁在跑、跑多快、占多少2.1 先找到目标进程ps 家族怎么选最直观的当然是ps aux但新手经常看走眼。这个命令输出里 VIRT、RES、%CPU、%MEM 这几列要分清VIRT 是虚拟内存很大不代表真的占了这么多RES 是物理内存%CPU 是进程在一段时间内占用的 CPU 比例。比如一个 Java 进程 VIRT 显示 20G 很正常因为 JVM 堆、直接内存、线程栈都会预留虚拟地址空间真正紧张的是 RES。要找最耗 CPU 的进程我建议直接用排序选项别一幅幅肉眼看ps -eo pid,ppid,%cpu,%mem,etime,cmd --sort-%cpu | head -20--sort-%cpu就是按 CPU 占用倒序想找内存最高就把字段换成--sort-%mem。这条命令比top好在它能一次性输出快照方便存文件做对比。pidof和pgrep的区别也值得说清。pidof nginx按二进制名字精确找pgrep -x nginx也是精确匹配进程名但pgrep -f python.*server.py是按完整命令行模糊匹配。模糊匹配虽然好用但要小心误伤——你搜一个关键字可能同时匹配到脚本、监控进程和日志收集进程。2.2 动态观察与 /proc 里的秘密top是动态视图常用键记住三个就够P按 CPU 排序M按内存排序c切换完整命令行。如果看多核负载按数字1。htop更友好可以直接按 F6 选择排序字段还能用 F5 看树形进程关系树形在定位“僵尸进程挂在谁下面”时特别好用。有些时候不能一直盯着终端我就用一条循环命令轮询某个进程的状态。假设 PID 是 1234while true; do ps -p 1234 -o pid,%cpu,%mem,etime,rss,cmd --no-headers; sleep 2; done实测下来这个写法非常稳特别适合观察内存是不是持续上涨。你还可以更进一步直接读进程的内核接口cat /proc/1234/status cat /proc/1234/cmdline | tr \0 ; echo/proc是 Linux 暴露内核状态的文件系统每个运行中的进程都有一个数字目录。看Status里的VmRSS和VmHWM能直接看到物理内存以及历史峰值排查内存泄漏时VmHWM往往是关键证据。2.3 给进程改名字、调优先级运维人的小技巧热搜词里有个“linux 修改进程名称”这确实是运维刚需。最常见的场景是服务器上跑了多个同名的 Java 服务ps里全是java根本分不清谁是谁。最简单的办法是在启动命令里用exec -a把 argv[0] 改成自定义名字bash -c exec -a my-order-service java -jar order-service.jar执行之后用ps aux | grep my-order-service就能直接认出来。另一个办法是直接往/proc/PID/comm里写名字但很多环境对这个文件有限制顺便提醒一句改comm只影响内核态看到的名称不影响实际二进制和命令行参数。优先级调整对应nice和renice。我用过最典型的一个场景凌晨跑批处理任务和线上业务抢 CPU导致接口抖动。解决方式就是把批处理进程降到低优先级renice -n 10 -p 2468这里有个坑普通用户只能把 nice 值往大调降低优先级只有 root 能往负数方向调。面试时这也是常考的点千万别弄反。3. 日志篇从日志堆里快速捞出黄金3.1 systemd 时代的 journalctl先学会按时间和单位过滤到了 systemd 之后几乎所有服务日志都被 journald 收走了。刚开始接触 journalctl 的人多半被它吓到其实核心就四个参数-u看某个服务--since/--until锁定时间窗-p按日志级别过滤-f跟随新日志。我常用的组合# 看 nginx 今天从早上到现在的日志 journalctl -u nginx --since today --no-pager # 看 30 分钟内的错误级日志 journalctl -u nginx --since -30 min -p err --no-pager # 实时跟踪 journalctl -u nginx -f如果你怀疑是内核或者硬件驱动的问题直接查内核日志journalctl -k -b -n 200 --no-pager-b表示当前启动周期排查“上次开机后就一直异常”的怪问题时很有用。记得加-T或者格式化输出否则看到的是单调递增的纳秒时间戳自己换算太费劲。3.2 传统日志文件与滚动策略别在日志目录里迷路虽然 journald 已经很普及老派日志文件依然大量存在尤其是自己写的应用和脚本。RHEL 系的/var/log/messages、/var/log/secureDebian 系的/var/log/syslog这些都是系统级日志。/var/log/secure里能看到 SSH 登录记录排查暴力破解、查看某用户何时登录都靠它。日志会无限增长吗不会logrotate 负责滚动。滚动规则定义在/etc/logrotate.conf和/etc/logrotate.d/下面。简单说就是按天、按周或按大小切分保留几份旧日志然后删掉更老的。排查时发现“报错记录明明在但当前文件找不到”多半是日志已经轮转走了去带.1、.2后缀的历史文件里找。这里推荐一个小技巧zgrep可以直接在 gzip 压缩的日志里搜关键字不用先解压zgrep ERROR /var/log/app/app.log.3.gz3.3 日志检索三步法切时间、找关键字、看上下文直接grep ERROR app.log在单机小日志上没问题但生产环境动辄几个 G 的日志文件这么干很容易把终端刷爆。我总结了三步法先切时间窗再定位关键字最后拿上下文。按照日志里时间戳字段先切出时间片awk /2025-01-05 12:1[0-5]/ app.log slice_1210_1215.log然后对这个切片做关键字检索grep -n OutOfMemoryError slice_1210_1215.log拿到行号之后用-A -B打印上下文grep -A 20 -B 5 OutOfMemoryError slice_1210_1215.log如果你要排查的是“某段时间前后到底发生了什么”连grep都不用直接sed -n /2025-01-05 12:10/,/2025-01-05 12:15/p app.log也能切片。区别是 sed 对超大文件更稳一些awk 处理字段更灵活。想看多个文件里的匹配grep后直接接app.log*即可会自动带出文件名。一个实际教训很多应用日志打印的是本地时间journald 用的是系统时间容器里又可能是 UTC。对不上时间轴时先确认时区必要时用date -d 时间戳把 Unix 时间戳转成可读时间。3.4 容器时代的日志docker logs 与 kubectl logs现在的日志排查早就绕不开容器。docker logs最常用的写法是docker logs --since 5m --tail 200 --timestamps my-container--since只看最近五分钟新增日志--tail控制返回行数--timestamps把容器内打印时间和宿主机时间对齐三件套一起上能有效避免日志量把终端刷爆。如果容器已经频繁重启记得先docker ps -a看容器 ID再用带--tail的参数拉别直接一把梭地docker logs否则几百 MB 日志能把终端卡死。K8s 场景对应kubectl logs多容器 Pod 要加-c指定容器名kubectl logs my-pod -c app-container --tail200 --since-time2025-01-05T12:00:00Z现实里很多慢查询和接口出错靠这个命令就能在几十秒内看到结果。容器日志最好选“打印到 stdout”的应用这是最符合云原生习惯的做法也省去进容器翻文件的功夫。4. 磁盘与文件篇满了、丢了、删不掉都怎么查4.1 磁盘占用排查df 和 du 的组合拳磁盘满是最常见的运维事故之一。第一步永远是df -hT df -i-hT里能看到文件系统类型和容量-i看的是 inode 使用率。很多人只盯第一行容量忽略了IUse%已经 100%——inode 耗尽的表现是明明磁盘空间还剩很多但创建不了新文件。这种问题多出现在缓存目录、消息队列目录小文件几十万个堆在一起把 inode 消耗殆尽。确定哪个分区满了之后用du逐层往下钻du --max-depth1 -h /data 2/dev/null | sort -hr | head -20--max-depth1只看一级子目录sort -hr按人类可读大小倒序。我见过很多人直接du -sh *以为够了其实数据量大时一条du -sh能跑很久而且输出太多没法一眼看关键。先看一级目录再顺着最可疑的目录往下钻效率高得多。定位大文件用findfind /data -type f -size 500M -exec ls -lh {} \; 2/dev/null这个写法把大文件列出来同时展示大小和路径。注意find可能因为无权限切入点报错把标准错误重定向到/dev/null可以避免刷屏。4.2 inode 耗尽与“删不掉”的文件lsof L1有一类极其经典的故障df -h显示磁盘用掉了 90% 以上但du -sh /*加起来远远小于这个值。这种时候基本可以肯定是有文件被删除但进程还持有句柄空间没有真正释放。怎么找用这条lsof L1 | head -30L1表示列出 link count 小于 1 的文件也就是被删除但仍被打开的文件。修复方法也比较简单确认是哪个进程占用了它比如正在写日志的进程然后按业务节奏重启或者让它重新打开日志。重启之后再看df -h空间通常就回来了。基于这个原理建议别在生产环境轻易rm一个正在被写入的日志文件。要清空日志应该用 app.log或者truncate -s 0 app.log而不是删掉重建否则磁盘空间可能一直不释放。4.3 挂载点与网络存储排查df -h看到的不只是本地盘很多时候还有挂载的网络存储比如企业里常见的 NAS 目录。排查这类路径时套路是一样的先确认挂载点是否处于正常状态再测该目录的读写。如果目录长时间没反应df -h都可能卡住这时可以在另一个终端执行mount查看挂载状态确认是否存在挂载错乱。这里提一句“Linux 挂载 NAS 存储”的热门场景。通常步骤是先创建挂载点目录再通过/etc/fstab配置持久挂载之后用df -h验证。如果你遇到的是一个莫名空目录或者说好的目录不显示优先检查挂载是否真的成功、进程是否还能读写该挂载点这比埋头查应用配置快得多。5. 深入取证lsof、strace、gdb 三件套5.1 lsof谁占着端口谁打开了哪条路的文件lsof是排查软件之间纠缠关系的神器但不少人只拿它查端口。查端口占用是它最基础的功能lsof -i :8080输出里 PID 和 COMMAND 两列是最关键的直接告诉你 8080 端口被谁霸占。如果是 root 权限还能看到进程的完整路径。想看某个进程打开了哪些文件使用lsof -p 1234 | wc -l这个数字如果异常高几千上万意味着进程可能一直在泄漏文件描述符。配合ulimit -n看最大文件句柄限制超过限制就会出现 “Too many open files”。这类问题常规日志看不出来但lsof一统计就明白。5.2 strace把进程的系统调用摊开看日志里什么都没有但进程就是不对劲这时候该看它到底在执行哪些系统调用。比如一个服务反复报“找不到配置”与其猜路径不如直接跟踪一次strace -f -e traceopen,openat,read -p 1234 21 | grep ENOENT-f表示跟随子进程-e traceopen只跟踪 open 相关的调用-t可以打印时间。每出现一个ENOENT就说明程序尝试打开某个路径但失败了这些路径会一行行列出来缺失的配置一目了然。strace 也能用来跟踪网络连接和内存分配但要注意一个很大的坑strace 会把目标进程拖慢很多倍尤其在系统调用频繁的程序上。实测下来一个高并发进程被 strace 挂上吞吐量可能瞬间掉一半以上。所以生产环境只在低峰期短时间用或干脆加-c只做系统调用统计不做逐条跟踪。5.3 gdbattach 现网进程的正确姿势gdb 调试常用命令是很多人搜过的话题。如果你要排查的是 C/C 服务的崩溃或者卡顿gdb 可以 attach 到正在运行的进程上把线程栈打出来。最安全的方式不是直接在线乱操作而是先用 gcore 抓一个核心转储文件然后离线分析gcore 1234 gdb /usr/sbin/nginx 1234.core如果确实需要直接 attach进入 gdb 之后少用交互式命令先把关键信息抓完就退出gdb -p 1234 -batch -ex thread apply all bt 2/dev/nullthread apply all bt是抓现网进程最高频的命令它能一次性打印所有线程的调用栈。看到某个线程卡在futex_wait多半是锁等待卡在read或write多半是 IO 问题。再看info threads和frame切换栈结合源码定位就八九不离十了。这里必须严肃提醒一句生产环境 attach 一个进程会短暂暂停全部线程对流量很大的服务可能造成瞬间抖动。能做 gcore 就先做 gcore不要在线上 gdb 里乱执行调试命令更不要在里面kill进程。我见过有人开着 gdb 忘了退出业务接口超时一片复盘才发现是调试工具把进程阻塞了。6. 面试与实战的“快问快答”6.1 面试现场最常出现的命令题Linux 面试题里几乎绕不开这几个场景我直接给出“满分回答链路”问一台服务器 CPU 跑满接口请求超时你怎么排查答先top -o %CPU找出最耗 CPU 的进程如果是自己的业务进程再看日志确认是不是流量突增如果是异常进程用ps -ef | grep PID看启动时间、命令行、资源占用。如果是 Java 进程用jstack结合 top 里的线程 ID 转十六进制定位到具体线程栈。问端口 8080 被占用抓不到是哪个程序怎么办答lsof -i :8080或ss -tlnp | grep 8080拿到 PID 后ps -fp PID看进程详情确认后再决定 kill 还是迁走。问磁盘满但du加起来空间对不上为什么答大概率是文件被删除但被进程持有执行lsof L1找出占用进程重启进程或服务后空间释放。问想确认某个应用运行中到底访问了哪些文件答strace -f -e tracefile -p PID过滤输出里的ENOENT和O_CREAT标志即可。这些问题考的不只是“命令背没背熟”更是在考“面对故障你有没有清晰的判断链路”。所以面试时别只报命令名把“为什么用这条命令、可能是什么问题”也带上。6.2 常见问题速查表症状首选命令关键点 / 注意磁盘空间满df -hT、du --max-depth1df 只是确认du 揪出罪魁祸首磁盘空间够但建文件失败df -iinode 满多为小文件过多文件删了空间没释放lsof L1重启占用进程前先确认业务影响端口被占用ss -tlnp、lsof -i :8080kill 之前先确认进程身份某进程 CPU 持续高top -o %CPU、ps --sort-%cpu结合线程栈分析根因服务日志找不到报错journalctl --since x -p err关注滚动后的旧日志文件某个程序读不到文件strace -e traceopen -p PIDgrep ENOENT 最直接进程崩溃求调用栈gcore、gdb -batch优先离线的 core 分析用户密码过期chage -l username、passwd -S username排查登录认证失败时顺便看一眼6.3 我的经验用“场景”去记命令最后说点个人体会。命令这东西光靠刷数据是记不牢的每一条命令第一次真正进脑子几乎都是在一次真实故障里。我建议你从明天开始遇到任何一个线上小问题哪怕只是个端口不通、日志报错都试着按这篇的“五连”思路走一遍看进程、看日志、看磁盘、看端口、必要时抓一次系统调用。不用多久你也能到半夜被叫起来时手不抖、脑不乱的程度。