这个系列写到现在第七篇,我得换个讲法了。前几篇我们按部就班地过了文件操作、权限、网络这些基础命令,很多朋友反馈说:命令背了不少,真到机器出问题的时候,还是不知道从哪下手。这其实不是记性差,而是缺一条"从现象到命令"的排查主线。所以这一篇我打算不按命令字典的套路来,而是直接用四个高频场景——进程异常、文件找不着、磁盘告警、负载飙高——把相关命令串在一起讲。适合刚入门想进阶的运维、开发,也适合准备Linux面试的兄弟,看完你会有一种"原来命令是这么组合用的"的顿悟感。
1. 进程管理:从ps aux出发,把进程看个明白
1.1 看进程的正确姿势
进程管理是Linux运维的日常,几乎所有问题都绕不开它。很多人习惯一条 ps -ef 打天下,这没错,但信息太少了,我建议你把 ps aux 的每一列都吃透。
ps aux 输出的几个关键列:USER是进程归属用户,PID是进程号,%CPU和%MEM是瞬时占用(注意是瞬时值,不太稳定,真要监控性能得看top或pidstat),VSZ和RSS分别是虚拟内存和实际驻留内存,STAT是进程状态,COMMAND是启动命令。
STAT这列很多人容易忽略,但它恰恰是判断进程是否健康的重点。R表示正在运行;S表示可中断睡眠(最常见,比如等I/O);D表示不可中断睡眠,一般是卡在磁盘I/O上,这时候kill都没用,只能等它自己缓过来;T表示停止;Z就是僵尸进程,子进程退出后父进程没回收,如果僵尸变多,说明父进程有bug。
实战里我更习惯用组合参数来定位问题,比如:
bash复制# 按CPU占用排序,找出最吃CPU的10个进程
ps aux --sort=-%cpu | head -11
# 按内存排序,看谁占内存最多
ps aux --sort=-%mem | head -11
# 只看某个用户的所有进程
ps -u nginx -o pid,ppid,%cpu,%mem,cmd
# 显示进程树,看清父子关系
ps -ef --forest
我做故障排查时,第一步永远是先跑一条 ps aux --sort=-%cpu | head,先看哪个进程在捣乱,再决定下一步用什么命令。这里有个小经验:ps aux 中的TIME列是进程累计消耗CPU的时间,如果某个进程 %CPU 看着不高、但 TIME 特别长,说明它已经在后台持续消耗资源很久了,同样值得注意。
1.2 进程的生命周期管理
查到了问题进程,接下来就是怎么处理。很多新手习惯直接 kill -9,这个做法我强烈建议改掉。
Linux的信号机制里,kill 默认发送的是15号信号(SIGTERM),这是让进程正常退出,进程可以自己清理资源、保存状态。kill -9(SIGKILL)是强制杀死,内核直接回收资源,进程没有机会做任何善后。直接 -9 的结果往往是:数据库没刷盘、配置没写完、临时文件残留,下一把就等着恢复数据吧。
我的习惯是分三步走:
bash复制# 第一步:先发SIGTERM,等几秒
kill 12345
sleep 3
# 第二步:检查进程是否还在
ps -p 12345
# 第三步:确认还活着再用-9
kill -9 12345
这个"给进程一个体面退场的机会"的原则,在线上环境尤其重要。另外还有几个常用配套命令:
bash复制# 按名字杀进程,注意killall会匹配所有同名进程
pkill -f "java.*order-service"
# 给进程发送自定义信号,比如让nginx平滑重载配置
kill -HUP $(cat /var/run/nginx.pid)
# 调整运行中进程的优先级(nice值范围-20~19,越小越优先)
renice -n -5 -p 12345
顺带说一句,top 里按 r 键可以交互式调整优先级,按 k 键可以直接输入PID发送信号,这个很多朋友不知道,实际操作起来比敲命令快。
1.3 冷门但实用:修改进程名称
这个技能在监控脚本、多实例部署、日志定位时特别有用。默认情况下,进程名就是启动命令的名字,但如果你用脚本启动Java服务或者Python任务,ps 里看到的可能是一堆 python main.py,根本分不清哪个是哪个。这时可以主动改进程名。
第一个方法:启动时用 exec -a 指定一个假名字。
bash复制exec -a my_custom_server python3 /opt/app/main.py
启动后 ps aux 里COMMAND列会显示为 my_custom_server,但真实的cmdline仍然能通过 /proc/PID/cmdline 看到。这个方法只在bash/sh里有效,当年我拿它区分同机部署的多套测试环境服务,实测很稳。
第二个方法:运行时修改 /proc/PID/comm。
bash复制echo "custom_name" > /proc/12345/comm
注意这个文件本身只允许写15个字符(内核限制),而且是线程级的概念,修改后 ps 和 top 里显示的进程名都会变。权限要求是必须有对应进程的写权限,一般得用root操作。
第三个方法:从代码层调用 prctl(PR_SET_NAME),像Python可以用 ctypes 调用libc的 prctl,适合想让程序启动后就自动改名。进程名修改对排查的意义在于:监控告警、日志采集、进程统计全部按新名字走,能够让脚本和人的认知对齐。但注意这只是"改名",不能真正改变进程的行为,也不能用来规避安全检测,别想歪了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 文件与内容查找:别老在全盘grep了
2.1 find:按时间、大小、权限精确过滤
排查机器的时候,最头疼的就是"文件不知道被放哪了"。如果直接 find / -name "*.log",在大型系统上可能要跑好几分钟,还扫出一堆权限报错。合理做法是用 -xdev 避免跨文件系统扫描,再用条件参数缩小范围。
bash复制# 查找根目录下的大文件,超过1GB的,不跨挂载点
find / -xdev -type f -size +1G -exec ls -lh {} \;
# 查找7天内修改过的文件
find /data -type f -mtime -7
# 查找10分钟前修改过的文件(排障时很常用)
find /tmp -type f -mmin -10
# 查找权限有异常的文件,比如不该有执行权的
find /data -type f -perm /111 -name "*.sh"
这里有个容易混淆的点:-mtime -7 是最近7天,-mtime 7 是恰好7天前那一天,-mtime +7 是7天以前。-mmin 同理但按分钟算。排障的时候,比如"刚改完配置但没生效",用 find /etc -type f -mmin -5 找出来谁被动了,一下就能定位到元凶。
find 配合 -exec 还能批量处理文件,比如批量改权限、批量压缩。不过需要小心:-exec 的结尾必须加 \;(或者用 + 表示把结果一次传入命令)。我以前看过有人把结尾写错,命令直接报错,记住这个细节。
bash复制find /var/log -name "*.log" -mtime +30 -exec gzip {} \;
2.2 内容检索:grep用熟了再试试rg
如果说find是找文件,grep就是在文件里找内容。日志排障、代码定位全靠它。
bash复制# 递归搜索目录,忽略大小写,显示行号
grep -rni "timeout" /app/logs/
# 关键字前后各显示5行,看上下文
grep -rn -B5 -A5 "ERROR" application.log
# 只统计匹配次数
grep -c "exception" app.log
# 匹配多个关键词
grep -E "ERROR|FATAL" app.log
我自己的经验:搜索源码和日志时别用 grep -r 直接扫,可能把无用的二进制文件、.git 目录全带进来,速度慢且噪音大。更稳的做法是限定文件类型,比如 grep -rn --include="*.py" --include="*.log" pattern /path,或者把不需要的目录排除掉:grep -rn pattern /path --exclude-dir=.git。
日志场景里还有个冷门好用的命令:zgrep,直接压缩包里搜内容。老日志都做了zip归档,不用先解压,直接 zgrep "ERROR" app.log.2.gz,快得很。
另外我要推荐一个替代工具:ripgrep(命令是 rg)。如果你还在用原生grep扫大型代码仓库,换成rg后你会怀疑人生——它默认忽略.git目录和二进制文件,自动遵守.gitignore规律,而且并行搜索速度快得不是一点点。Rust写的工具,很多发行版直接 apt install ripgrep 或 dnf install ripgrep 就能装。我的使用习惯:日常临时排障用grep,进入大型代码库定位问题时一定用rg。
3. 磁盘与存储:df、du与NAS挂载
3.1 弄清楚空间去哪了
磁盘告警几乎每个运维都遇到过。告警之后第一步是 df -h,看清整体占用,但 df 只知道哪个分区满了,不知道是谁占满了。
很多人立刻去 du -sh /,这其实等于做了一次全盘扫描,慢。更聪明的方式是逐层定位:
bash复制# 画风一转,在可疑的分区直接看根目录每个子目录的大小
du -sh /data/*
# 也可以指定深度,一次看两层
du -h --max-depth=2 /data | sort -rh | head -20
我管这个叫"洋葱式排查",一层层进去,直到锁定最占空间的目录。需要注意的是:du 统计的是目录里所有文件实际占用的块大小,而 df 统计的是文件系统已用空间,两者在逻辑上有差异,比如有文件被删除但还没释放、或有隐藏挂载点时,数值会不一致。
还有一个高频误判:开发说"我把大文件删了,怎么磁盘还是满的"。这时候多半是有进程还持有这个被删除文件的句柄。查证方法:
bash复制# 找出被删除但仍被占用的文件
lsof | grep deleted
# 拿到PID后,强制让进程释放文件
# 可以用 /proc/PID/fd/ 里对应的句柄
ls -l /proc/12345/fd/
通过 /proc/PID/fd/N 里的软链,可以看到文件虽然显示"deleted"但仍处于已打开状态。处理办法是重启进程,或者用 : > /proc/12345/fd/N 清空该句柄指向的文件内容,让空间立即释放。
3.2 NAS挂载实战:mount和fstab
服务器空间规划里,把一部分数据放到NAS存储是很常见的架构,这就涉及网络挂载。Linux下挂载NFS的整套操作,值得认真写一下。
bash复制# 先看看服务端导出了哪些路径
showmount -e 192.168.1.100
# 创建挂载点并挂载
mkdir -p /data/nas
mount -t nfs 192.168.1.100:/volume1/nfs-backup /data/nas
# 验证是否成功
df -h | grep nas
挂载有几个常用参数值得注意:rw 读写挂载,noexec 禁止执行二进制(安全考虑),nfsvers=3 或 nfsvers=4 显式指定NFS版本(有些老NAS默认v3,新环境v4性能更好,版本不对会挂载失败),tcp 指定传输协议(默认就是tcp),_netdev 表示等网络就绪后再挂载(写在fstab里时特别有用)。
如果希望重启后自动挂载,就得把条目写进 /etc/fstab:
text复制192.168.1.100:/volume1/nfs-backup /data/nas nfs defaults,_netdev,nfsvers=4 0 0
写完建议先执行 mount -a 验证配置没问题,再考虑重启。fstab写错导致开机进不去系统的案例太多了,所以重点是"先验证,再持久化"。
取消挂载时如果提示"target is busy",多半是有进程在占用。排查和强制处理:
bash复制# 查看谁在用挂载点
fuser -vm /data/nas
lsof +f -- /data/nas
# 强制解除占用并卸载
fuser -km /data/nas
umount /data/nas
我再补一句:生产环境挂载NFS,超时参数很重要。NFS默认可能长时间等待无响应服务端,加上 timeo=30,retrans=2 这种参数能避免应用卡死。这块属于"不踩一次坑都不知道"的经验,写在这里给各位提个醒。
3.3 文件删不掉、空间不释放的延伸案例
这个坑我在线上遇到不止一次了。现象是:df -h 显示 / 分区99%,但 du -sh /* 全部加起来也不到50%。排查思路要跳出普通目录,依次检查:被进程占用未释放的文件、隐藏挂载点、已删除但被打开的大文件。
bash复制# 按被打开文件的大小排序,找出那些没释放的大占用
lsof +L1 | sort -k7 -rn | head -20
方案通常是重启对应进程,如果进程不能重启,就用 /proc/PID/fd/N 配合清空文件的方式应急。这套组合拳掌握后,再难的"空间谜案"也能快速破案。
4. 系统性能排查:负载高的时候到底在忙啥
4.1 三张表判断问题在哪
负载(load average)是Linux排查的核心指标。很多人一看 uptime 出现 load average: 20 就慌了,这里得先理解它的含义:它表示运行队列中等待CPU、等待I/O等状态的进程平均数量。它不等于CPU占用率,一个多核机器负载数值高于CPU核数,才说明系统处于过载状态。
排查负载高,我习惯同时抓三张表:
bash复制# 第一张:看整体负载和历史趋势
uptime
# 第二张:每秒刷新一次,累计5次,看CPU状态分布
vmstat 1 5
# 第三张:看具体进程聚集在什么调用上
top -b -n 1
vmstat 输出要重点看这几个字段:r(运行队列),us(用户态CPU)、sy(内核态CPU)、wa(I/O等待)、si/so(交换分区的换入换出)。我自己的判断口诀是:如果 us 很高,说明是计算密集;如果 wa 很高,说明是磁盘I/O瓶颈;如果 si/so 高,说明内存不够在疯狂换页,后两种机器反应缓慢的体感明显。
4.2 谁占CPU、谁占内存
定位到是CPU瓶颈后,再用 top 进入交互模式,按 P 键按CPU排序,按 M 键按内存排序。观察一段时间,看是哪个进程持续占用,还是在多个进程间跳动。
如果是Java应用占用高,就得抓线程栈了:
bash复制top -Hp 12345
printf '%x' 12346
jstack 12345 | grep -A30 'nid=0x...'
把线程PID转成十六进制,再到jstack输出里找对应线程,就能直接定位到代码行。这一套对Java性能排查效果极佳,建议做Java开发的朋友背下来。
内存方面,看 free -h 别只看已用的数值,Linux内核会尽量利用空闲内存做缓存,所以重点是看 available 列,这才是应用真正可用的内存量。如果 available 很小但 used 也不大,说明cache很多,不必紧张;如果swap使用了且swap一直是满的,就用上面的 si/so 判断内存是否真的吃紧。
4.3 端口和连接:ss、netstat、lsof
排查网络问题时,传统工具 netstat 现在很多系统不再默认安装了,官方推荐用 ss。它们用法类似,但 ss 速度更快、信息更准。
bash复制# 查看所有监听端口及其对应进程
ss -tlnp
# 查看所有TCP连接统计
ss -s
# 查看具体端口被谁监听
ss -tlnp | grep 8080
# lsof直接看端口对应进程
lsof -i:8080
连接数异常也是排查常见场景,比如某个服务连接数飙高,可以先统计连接状态分布:
bash复制ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn
再看具体哪些远程地址连过来:
bash复制ss -tan | grep 8080 | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head
这套命令在排查"端口被抢""连接数打满""服务假死"时非常有用,基本一两分钟内就能判断是突发的流量攻击还是配置错误。
5. 平时容易踩的坑:问题排查实录
5.1 磁盘快满了,却找不到大文件
典型现场:df -h 显示 / 用满,但 du -sh /* 算出来只用了60%。排查顺序我建议固定成三查:一查有没有被删除但仍占用的文件(lsof | grep deleted),二查有没有新的挂载点把整个目录盖住(mount | grep /data),三查是不是inode耗尽(df -i)。其中inode耗尽很容易被忽略,df -h 看着还有空间,但创建文件就报"No space left on device",这时用 df -i 一看,inode使用率100%,清一批小文件问题就解了。
5.2 进程kill不掉
进程 kill -9 也杀不死时,先看系统负载和进程状态。处于D状态的进程(典型的是卡在NFS挂载点或磁盘I/O上)会一直不可杀,处理方法是先让底层I/O恢复,比如手头正好有卡住的NFS挂载就先去恢复网络存储,或者重启机器,不要反复用 kill -9 硬打。另外,僵尸进程(Z状态)是已经死了的进程,杀不掉是因为父进程没有回收,正确做法是找有没有父进程存在,如果父进程还活着就重启它(或者用 kill -HUP 让它重新读取子进程状态),而不是对僵尸进程本身发信号。
5.3 命令找不到,先看PATH
command not found 并不代表命令真不存在。有一次我一个同事说"服务器上curl都没装",结果一查 /usr/bin/curl 静静躺在那里,问题是他的脚本里把PATH环境变量覆盖了。排查习惯:碰到找不到命令,先 echo $PATH 看看当前搜索路径,再用 which、whereis 查真实位置。验证是否安装用 ls /usr/bin/curl 比 curl --version 更靠谱,前者不受PATH影响。这个坑看起来低级,但实际发生频率很高,值得写进你自己的排障清单。
5.4 做实验的两道保险
学命令最怕的就是拿生产环境练手,我建议所有准备上线的操作,先用 echo 或 ls 打印一下命令将影响的范围再执行。比如要批量删除文件:
bash复制find /data/logs -name "*.log" -mtime +7 -print
# 确认输出无误后,再换成 -delete 或 -exec rm {} \;
把 -delete 先换成 -print 看一遍,这是最便宜的安全措施。另外,日常在多台服务器操作时,可以给登录别名加上颜色和主机名提示,避免"A机器上改了B机器的配置"这种低级事故。
我记得刚开始学Linux那两年,最喜欢做的事就是把命令一篇篇收藏下来,但真到用的时候还是两眼一抹黑。后来发现问题的根源不在命令量,而在缺少把命令串成流程的"场景感"。所以这个系列我越来越倾向于按场景写,比如这篇就是围绕进程、查找、磁盘、性能四个方向,解决的是"第一步看什么、下一步干什么"的问题。查命令本身永远来得及,难的是形成自己的排查直觉。建议你从现在开始,就把今天这几个组合键当成自己的"默认反应",在测试机器上多演练几遍,等真正锈住的螺丝出现在你眼前时,你会感谢这些肌肉记忆的。
