先不管标题为什么把 Linux 打成了“Liunx”,反正大家在各种搜索引擎里都这么敲,能搜到就好。这篇文章是《小白实战学习 Linux》系列的第六篇,前几篇我们把文件操作、权限管理、用户这些基础都过了一遍,这一篇我想换个角度,把重点放在系统本身——当你拿到一台 Linux 机器,怎么知道它在想什么,又怎么在它出问题的时候快速找到元凶。尤其是你以后要部署网站、跑脚本、搭服务,一定会遇到“系统突然变慢”“CPU 飙到 100%”“端口被占用了”这类问题,这时候光会 ls、cd 是不够的,你需要一套系统状态查看和进程管理的实战手段。
这篇内容不适合完全没碰过终端的人,但只要你跟着前面几篇练过命令,哪怕只是会敲 cd、ls,也能顺畅地把全文读完。我会尽可能绕开复杂的原理,用最直白的方式解释那些看起来吓人的专业术语,然后把每一段可以照抄执行的命令都写成可以直接复制粘贴的格式。遇到关键地方,我会顺手聊聊我自己的踩坑经历,这些通常才是文档里不会写的东西。
1. 先从一次“系统突然变慢”的现场说起
大概两年前,我被叫去帮朋友看一台服务器,现象非常典型:网站能打开,但是特别慢,点击一个页面要转圈十几秒,ssh 登上去之后敲命令都有明显延迟。正常情况下,一台 Linux 机器出现响应变慢,百分之八九十的问题不是出在“网络”,而是出在“资源被耗尽了”。
我当时做了一件事:在终端输入 top,回车。屏幕上瞬间弹出一堆刷新的数据,朋友问“这啥意思”,我说这就是 Linux 的体检仪。在 top 的输出里,通常第一眼就能看到罪魁祸首——某个进程的 CPU 占用率直接干到了 90% 以上。顺着 PID 查了一下,是一个没退出干净的脚本在无限循环,最后把它结束掉,系统立刻恢复。
这次经历让我意识到一个很朴素的道理:在 Linux 里,几乎所有看起来“玄乎”的问题,都有可以定位的线索,关键在于你知不知道去哪里看。top、ps、free、df、kill 这几个命令,就是排查问题的主干道。只要把这一套流程练熟了,小白也能像一个有经验的运维一样,在几分钟之内找到问题源头。
对于刚入门的同学,我建议不要一上来就啃《深入理解 Linux 内核》之类的书,而是把你自己的机器当成一个病人,学会“望闻问切”——看看它的 CPU、内存、磁盘、进程状态,了解它是不是健康。这也是本篇所有内容的出发点。
2. 读懂系统状态,先学会看这几张“体检表”
2.1 平均负载(load average)到底在说什么
很多初学者第一次看 uptime 命令,会被输出里的 load average 搞懵。它显示三个数字,比如 load average: 0.50, 0.36, 0.28,听起来像某种评分,其实它的意思是“在过去 1 分钟、5 分钟、15 分钟内,系统里正在运行和等待 CPU 处理的进程平均数量”。
可以把它理解成“排队人数”:如果你的系统有 4 个 CPU 核心,load average 是 4,意味着刚好填满。如果是 8,说明平均每个 CPU 核心有 2 个任务在排队。判断系统是否过载,不能只看数字大小,要看和 CPU 核心数的比值。所以标准姿势是:先用 nproc 看看你有几颗 CPU,再和 load average 对比。
我自己习惯先看 15 分钟那个数字,因为它比较稳定,能反映一段时间的整体压力;1 分钟的数字会剧烈跳动,容易把人吓到。如果 1 分钟远高于 15 分钟,说明负载是刚刚飙起来的,这时候赶紧去 top 里找是谁干的。如果 15 分钟也很高,那可能问题持续很久了,比如某个程序一直在空转。
2.2 内存和磁盘的使用情况,一分钟内扫完
看内存用 free -h。注意 -h 选项的作用是“人性化显示”,把字节数转成 G、M 这样的单位,否则你会看到一串吓人的大数字。输出的第二行写的是 available,这比 used 列更有参考价值,因为 Linux 会把空闲内存拿去做缓存,available 才代表“真正可以给你的程序用的内存”。
看磁盘用 df -h。重点看根分区 / 的 Use% 是不是接近 100%。我印象最深的一次事故就是根分区被日志打满,服务直接挂掉。当时某个程序写日志的速度远超预期,几天时间就把磁盘吃光了。所以日常巡检我建议至少每周看一眼 df -h 的输出,别等磁盘满了再来求人。
这张“体检表”不只包含命令本身,更重要的是你要有横向对比的意识。CPU 高可能是计算密集,内存高可能是程序泄漏,磁盘满可能就是日志忘清理了。每类资源都有自己的典型症状,后面第 5 节我会拿一个完整案例串起来演示。
3. 进程管理三件套:查看、定位、处理
3.1 ps 命令:给进程拍快照
top 是持续刷新,ps 则是“拍一张照片”。排查问题时我通常先用 ps 做初步筛选,因为它可以直接配合 grep 来做精确查找。
最常用的组合是ps -ef和ps aux,这两个都能列出所有进程,只是格式略不同。我习惯用ps -ef,因为第二列就是 PID,方便下一步操作。如果要在里面找某个关键词,直接接管道加 grep。
比如我想看有没有运行 nginx 相关的进程,就敲:
ps -ef | grep nginx这里有个经典的小坑:如果你搜某个进程名,经常会在结果里看到一条 grep 自己的记录。比如上面的命令输出里可能会有一条grep --color=auto nginx,这是 grep 命令自己匹配到了关键词。用ps -ef | grep nginx | grep -v grep可以过滤掉,或者直接忽略它。当然,现代系统里更推荐用 pgrep :
pgrep -l nginxpgrep 只输出 PID 和进程名,干净利落。需要杀进程的时候还可以直接用pkill 进程名,但我的习惯是先用 pgrep 拿到 PID,再用 kill 加 PID 处理,这样可控性更强。
3.2 top 命令:实时刷新,动态观察
如果 ps 是拍照片,top 就是录像。启动 top 之后,默认每三秒刷新一次,屏幕上半部分是整个系统的状态汇总,下半部分是进程列表,按 CPU 使用率从高到低排序。
top 里几个按键值得记一下:
P:按 CPU 占用排序,默认就是,如果不小心按乱了可以按回来。M:按内存占用排序。k:提示输入 PID,输入后可以给指定进程发信号,相当于在界面内直接执行 kill。q:退出。
我看到很多教程直接让人跑 top,然后盯着数字发呆。我更建议刚开始学的时候先看前五行的汇总信息,尤其是 %Cpu(s) 这一行的 us 和 sy 两列。us 代表用户态 CPU 占比,sy 代表内核态 CPU 占比。正常情况 us 高一点没什么,但如果 sy 经常超过 30%,就要小心是不是程序在疯狂调用系统接口,比如频繁读写文件、频繁创建进程。
下面是一个标准的 top 输出片段,我在关键字段上加了说明:
top - 22:15:33 up 2 days, 4:11, 1 user, load average: 0.85, 0.42, 0.30 Tasks: 123 total, 1 running, 118 sleeping, 0 stopped, 4 zombie %Cpu(s): 18.5 us, 2.3 sy, 0.0 ni, 79.0 id, 0.2 wa, 0.0 hi, 0.0 si MiB Mem : 1984.4 total, 1309.1 free, 356.6 used, 318.7 buff/cache MiB Swap: 2048.0 total, 2048.0 free, 0.0 used, 1024.2 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1234 root 20 0 256456 53212 10132 R 85.3 2.6 15:33.92 some_demo看到 Tasks 里有 zombie 这个词别慌,它是“僵尸进程”的标记,后面第 6 节专门讲。如果你只想看某个用户或某个关键词的进程,可以进 top 后按u再输入用户名,或者按c展开完整命令行,这些技巧排查真实问题时效率极高。
3.3 kill 家族:结束进程也有讲究
很多新手把 kill 理解成“强杀”,其实它的本质是“给进程发送一个信号”。默认不带参数执行 kill PID,发送的是 SIGTERM(编号 15),这个信号相当于礼貌地告诉进程:“请你正常退出。”大多数程序收到这个信号会自己清理临时文件、保存状态、从容退出。
如果进程“不听话”,SIGTERM 之后还赖着不走,再考虑用kill -9 PID。-9 对应 SIGKILL,这个信号是强制性的,内核直接帮进程“断气”,完全不给它善后的机会。所以我的原则是:先kill PID,等两三秒,看进程还在不在,再用kill -9 PID补刀。
遇到同名进程需要一起清理时,pkill 和 killall 会方便很多。但也要小心,pkill 是按“部分名字匹配”的,用不好会误杀。比如你输入 pkill sick,它会连 sick_service 和 sickd 一起杀掉。我以前就干过这种事,本来想杀一个临时脚本,结果把同名的服务进程全带走了。所以稳妥做法还是 pgrep -l 看清楚,再精确地按 PID 杀。
4. 后台任务与任务控制:别让程序把终端“绑死”
4.1 前台、后台与 Ctrl+Z
我们平时在终端敲命令时,命令默认在前台运行。前台运行意味着这个终端窗口被占住了,你要等它执行完才能敲下一条命令。遇到执行很慢的命令,比如打包一个大目录、跑一个数据处理脚本,整个人就只能干等着,非常浪费时间。
解决办法是让任务在后台运行。最简单的方式是在命令末尾加一个&符号:
sleep 100 &这样命令会在后台运行,终端立刻返回可用状态。用 jobs 可以查看当前终端窗口往后台丢了哪些任务。输出里会显示任务的编号,比如 [1]- 表示第 1 个后台任务。用fg %1可以把它调回前台,用bg %1让它继续在后台跑。
另一个很实用的场景是:任务已经在前台跑起来了,你突然不想干等,怎么办?可以按Ctrl+Z把当前任务挂起(暂停),然后输入bg让它转到后台继续跑。我经常用这个组合来“抢救”一个不小心弄成前台执行的大任务。
4.2 nohup 和标准输出重定向
&有个致命弱点:命令还是会和当前终端绑定。如果你直接关掉终端窗口,后台任务很可能会跟着挂掉。要解决这个问题,得靠 nohup。它的作用是让命令忽略终端挂断信号,就算你退出登录,它也照样跑。
我推荐的标准写法是这样的:
nohup sh long_task.sh > task.log 2>&1 &拆解一下:nohup 负责脱离终端生命周期;> task.log把标准输出重定向到文件,否则 nohup 会把输出写进一个叫 nohup.out 的文件里,容易把磁盘搞乱;2>&1的作用是把报错信息也塞进同一个日志文件,确保程序报错了你也能看到。如果忘记加这一串,程序跑一半崩溃了,你又没有日志可查,排查起来会非常痛苦。
这里顺带说一个很多新手常犯的错:以为加了&就万事大吉,结果关掉终端再回来发现任务没了。现在系统已经提供了更完善的方案,比如 systemd 服务,但作为过渡,nohup 还是那个“上手最快、零依赖”的临时方案。
5. 现场排障实录:一次 CPU 飙高问题的完整排查
纸上得来终觉浅,我模拟一个非常典型的真实场景,从发现问题到解决问题走一遍完整流程。假设你收到告警,某台机器的 CPU 已经持续高负载十分钟了。你 ssh 登上去,按照下面这套顺序排查。
第一步,先看整体压力。执行 uptime,确认 load average 是否异常偏高。假设输出里的 load average 是 6.35,而这台机器只有 2 核,说明平均每个核心上有三个人在排队,确实是过载状态。
第二步,用 top 按 CPU 排序,观察是谁在消耗。典型的输出里会有一个进程,比如 COMMAND 列叫 some_demo,%CPU 列显示 180%,占满了整整一个多核。用 top 的k功能直接处理也行,但为了能看清它的“底细”,我习惯先记下 PID,然后退出 top。
第三步,查看这个进程的详细信息。Linux 在 /proc 目录下为每个进程建了一个以 PID 命名的目录,里面有很多有用的文件。比如:
cat /proc/1234/status重点看 Name 和 State 字段,能确认进程的实际名字和当前状态。想查看它的完整启动命令行,可以看 /proc/1234/cmdline,这个文件内容可能包含启动参数。如果进程是你熟悉的程序,看到参数基本就能判断它是不是被错误地启动了。
第四步,做决定。如果确认是无用进程,先执行kill 1234,等几秒再去 top 里看它是否消失。如果它变成了僵尸状态或者还霸占着 CPU,再执行kill -9 1234。
第五步,验证。再跑一次 uptime,观察 load average 是否逐步下降。这里注意,负载数字不是瞬间归零的,15 分钟那个值会缓慢回落,因为它是滑动平均值。很多人杀完进程看到 load average 还是高,以为没成功,其实是还没刷新完,稍微等几分钟再看。
这套流程看着简单,但很多初学者会卡在“不知道杀谁”。我的建议是永远不要只凭一个 COMMAND 名字就动手。先看完整命令行,再想想这个进程是怎么来的。Linux 下动手杀进程之前多花十秒确认,很可能就避免了一台线上服务器的误杀事故。
6. 常见问题与避坑速查表
6.1 僵尸进程:看着吓人,其实没那么难处理
top 输出里如果出现 zombie,很多人会被吓到,以为服务器中毒了。僵尸进程其实是一个已经结束、但还没被父进程“收尸”的进程。它不再占用 CPU 和内存,但会占一个 PID 名额。
处理僵尸进程的关键在于它的父进程。执行下面的命令查看父进程是谁:
ps -o pid,ppid,stat,cmd -p PID如果父进程是 init 或 systemd,它一般会很快去清理僵尸。如果父进程是你自己跑的一个常驻脚本,就得想办法让它结束对子进程的等待,或者重启这个父进程。最常见的坑是:你直接把僵尸进程的 PID 用 kill -9 杀掉,结果根本没反应——因为它本来就死了。要处理的是那个“还没收尸”的爸爸,不是僵尸本身。
6.2 端口被占用:一句话定位开枪的人
部署服务时,“端口被占用”是最常听到的报错。查端口的命令组合是 ss 加上 -tlnp 参数。比如查 8080 端口:
ss -tlnp | grep 8080输出里有 pid 和 process 字段,能直接看到是哪个程序占了端口。如果没有 ss,用lsof -i:8080也可以,但某些精简安装的系统没带 lsof,反而不如 ss 通用。拿到占用端口的进程之后,按上面第 3 节的办法结束它,或者如果你确实需要这个端口且不想杀进程,那就把新服务的端口改掉,这就是一场谁弱谁让步的博弈。
6.3 内存只剩几十兆,怎么快速“找凶手”
free -h 看完发现内存快满了,先别急着杀东西。如果 swap 已经被大量使用,而可用内存很低,说明内存确实吃紧。这时候用ps aux --sort=-%mem让进程按内存占用降序排列,第一屏就是“内存大户”。看到逗留的缓存的存储机制、大型 Java 进程或者数据库,这些都是合理的高占用者。
不过内存问题的根源往往不是某一个进程,而是“多个中等的进程堆叠”。所以更稳妥的排查方式是把前十个内存大户列出来,逐个确认是不是必要的,而不是盯着最高的那个杀。另外,如果只是为了缓解一时之急,重启那个内存泄漏的服务往往比杀进程更有效。
6.4 为什么说 kill -9 是最后手段
很多人把 kill -9 当成万能杀器,这其实是运维新手最容易犯的毛病。前面已经解释过,-9 是 SIGKILL,进程没有任何机会进行善后处理。对于数据库这类需要保证数据一致性的程序,kill -9 可能导致数据文件损坏;对于正在写入内容的进程,直接-9 可能留下一堆临时文件。
所以规范顺序一定是:kill(SIGTERM)→ 等几秒观察 → 确认没死 → 再 kill -9。这条规则我反反复复踩了很多次坑才真正记住,因为早期我觉得“杀个进程哪来那么多讲究”。
7. 文件与进程之间的纠缠:一条命令看清谁在读写文件
最后补一个特别实用的技能。有时候你怀疑某个程序在疯狂写日志把磁盘占满了,想找出它打开了什么文件,可以用 lsof。这个命令的全称是 list open files,Linux 下一切皆文件,lsof 能看到进程打开的所有文件。
比如查看 PID 1234 这个进程打开了哪些文件:
lsof -p 1234输出里会列出文件描述符和路径。如果发现某一路径的文件一直在变大,那就是“吃磁盘”的元凶。反过来,如果想知道某个文件正被谁占用,比如你想卸载一个目录却提示“设备忙”,就可以用lsof +d /路径来找占用者。这个命令在应对“无法卸载磁盘”这类问题的时候非常管用。
不过 lsof 在某些精简环境里没有预装,需要先安装对应软件包。不想安装的话,可以用ls -l /proc/PID/fd查看文件描述符号,虽然输出格式没那么友好,但关键时刻能救场。把这条也算进你的“Linux 急救包”里吧。
8. 进程优先级:让重要的事先做
进程管理里还有一个概念值得顺手记一下:进程优先级。Linux 规定优先级数值范围是 -20 到 19,数字越小优先级越高。也就是说,-20 是“最高优先级”,19 是“最低优先级”,和我们平时的直觉恰好相反。
让一个程序以特定优先级启动,用 nice 命令。比如我希望某个备份脚本别跟正式服务抢 CPU,就用:
nice -n 10 sh backup.sh如果程序已经在运行了,用 renice 调整优先级,注意需要相应权限:
renice 10 -p PID普通任务别轻易调成负优先级。我见过有人图省事把所有任务都调到高优先级,结果谁也不让谁,系统反而更卡。合理用法是把非关键、批量处理的任务调低,保证重要服务响应更快。
最后再分享一个实用技巧
排查问题时,最怕的不是不知道命令,而是被海量输出淹没。有一个小习惯我一直到现在都在用:每跑完一个排查命令,先把输出里最关键的一列记在手边的小本子上,比如 PID、%CPU、路径。不要直接凭记忆操作,因为你可能下一秒就又去敲别的命令了,回去想找那个 PID 就找不到了。
另外,像 top 这类交互式命令,几乎天天都在用,它的配置可以写成文件,在启动时指定。但小白先不用折腾这些,先把ps -ef | grep和top + k练到条件反射的程度就够了。系统管理这门手艺,靠的是手感,而手感来自重复动手,不是背参数表。每天挑一个常见的小问题,按“查看状态、定位进程、确认信息、处置”四步走一遍,不出一个月,你再看以前那些“服务器又卡了”的求救帖,心里就有谱了。