1. 系统管理命令的定位:平时是工具,故障时是救生圈
很多人刚开始接触 Linux 的时候,和我当初一样,觉得系统管理命令就是一堆零碎的小命令——ps 看看进程、free 看看内存、df 看看磁盘,好像没什么技术含量。这个认知得改一改。真正在线上环境待过一段时间就会明白,这些命令是系统管理员的基本功,它们组合起来能解决绝大多数日常和应急问题,而且每一类命令背后都对应着一套完整的系统机制。把命令本身背下来容易,把机制理解透、知道什么场景该用哪个,才是真正值钱的部分。
我印象很深的一次排障经历是这样的:某天凌晨,某项目的监控系统发来告警,核心应用接口的响应耗时飙到正常值的十倍以上。远程登录服务器,第一眼就看到 top 输出的 load average 超过了 CPU 核数,CPU 基本被打满。这时候如果对着进程列表一个个翻,效率太低了。我直接跑了ps aux --sort=-%cpu | head,一眼就看到有个 Java 进程的 CPU 占用异常高,接着用top -Hp <PID>找到具体线程,配合线程栈信息,最后定位到是连接池参数配置不当引发的连接风暴。整个过程不到一刻钟。
要是平时没有把这些命令练熟,这种场景下只能干瞪眼,然后被业务方在旁边连环催。所以这篇文章打算把系统管理命令按场景重新整理一遍:进程管理、用户与权限、资源观测、服务管理、故障排查。我不打算照着 man 手册逐条翻译,而是结合实际使用经验讲清楚每个命令解决什么问题、关键参数怎么选、都有哪些坑。适合刚开始接触 Linux 的初学者建立系统管理的基本框架,也适合有一定基础、想系统化梳理命令体系的开发者做一次查漏补缺。
2. 进程管理:ps、top、kill 组成的完整武器库
2.1 先把 ps 的分寸摸清楚:两种流派与排序技巧
ps 是查看进程时使用频率最高的命令,但很多人只记住了ps aux,却不知道它和ps -ef有什么不同。简单说,ps aux是 BSD 风格输出,ps -ef是 System V 风格输出,两者列出的信息基本一致,区别主要在字段排列和对齐方式。在脚本里解析进程信息时,用ps -ef更稳定,因为它的输出格式相对固定;人工排障时,我习惯用ps aux,因为它能直接配合--sort参数做排序,效率高一截。
比如说,想马上知道哪个进程在吃 CPU,直接跑:
ps aux --sort=-%cpu | head -10注意--sort=-%cpu前面的负号表示降序,想按内存排就是--sort=-%mem。这个用法在排障现场特别实用,比top更适合批量抓取信息,因为它是非交互式输出,可以直接管道给其他命令处理。
还有一个很经典的坑:很多人排查进程用ps aux | grep java,结果 grep 自己也出现在结果里。虽然能用grep -v grep过滤,但更干净的做法是改用pgrep -f java,或者直接用pidof。另外,ps 默认输出的 COMMAND 列只有一屏宽度,进程启动参数太长会被截断。排查启动参数类问题时需要看完整命令行,这时候要加-w参数,写成ps auxww或者ps -efww,多一个 w 让输出不换行不截断。
进程状态的解读也值得专门说一句。ps aux输出里的 STAT 列,常见的状态码含义如下:
| 状态码 | 含义 | 常见场景 |
|---|---|---|
| R | 运行中或可运行 | 正在消耗 CPU 的进程 |
| S | 可中断睡眠 | 等待事件、网络、IO 的进程 |
| D | 不可中断睡眠 | 通常在做磁盘 IO,不能被信号打断 |
| Z | 僵尸进程 | 子进程已结束但父进程未回收 |
| T | 停止 | 被 SIGSTOP 或 Ctrl+Z 挂起 |
| < | 高优先级 | nice 值为负 |
| N | 低优先级 | nice 值为正 |
如果你看到一堆 Z 状态的僵尸进程,说明这些子进程已经结束,但它们的父进程没有调用 wait 系统调用来回收资源。这多半是程序有 bug,或者父进程异常退出导致的。在线上看到大量僵尸进程,首要动作不是 kill 僵尸本身(它们已经算"死"了),而是找到它们的父进程,把父进程处理掉,让系统自动清理遗留的僵尸。
2.2 top 的交互式排障:不只是看一眼 CPU
top 是实时性能观测的第一站,但它远不止"看一眼 CPU 百分比"这么简单。进入 top 界面后,有几个快捷键必须形成肌肉记忆:
- 按
P:按 CPU 占用排序 - 按
M:按内存占用排序 - 按
T:按累计 CPU 时间排序 - 按
k:输入 PID 可以直接发信号给进程 - 按
r:调整进程的 nice 值 - 按
f:选择要显示的字段
top 界面第一行的 load average 是很多人的迷惑点。它依次表示最近 1 分钟、5 分钟、15 分钟的系统负载。判断负载是否异常,要把这个数字和 CPU 核数做对比:一台四核服务器,load average 到 4 左右说明 CPU 基本吃满;超过 4 意味着有进程在排队等待 CPU 资源。但光看这个数字不够,因为 load 不仅包含 CPU 排队,还包含不可中断睡眠状态的磁盘 IO 等待。如果发现 load 很高,但 top 里 CPU 的 us 和 sy 占比都不高,那多半是 IO 瓶颈而不是计算瓶颈。
还有个小技巧:top -Hp <PID>可以查看某个进程内部的所有线程,这在排查 Java、多线程 C++ 程序时是基本操作。你看到一个进程整体 CPU 高,但不确定是哪段代码的问题,先用这个命令找到具体是哪个线程在跑,再用对应的线程分析工具去抓线程栈,定位就精准多了。
2.3 kill 家族的信号语义:先礼后兵
很多人用 kill 就是kill -9,这是新手最容易踩的坑。kill 命令的本质是发送信号,而不是直接强制结束进程。-9对应 SIGKILL,是内核直接回收进程,不给进程任何清理机会。对于大部分服务进程,更合适的做法是先发 SIGTERM,也就是默认的kill PID,对应信号 15,让进程有机会做资源清理、保存状态、优雅退出。尤其是涉及持久化数据的程序,直接 SIGKILL 很可能会留下脏数据,下次启动时还得做一致性恢复。
需要按名称批量杀进程时,可以用 pkill,它支持按进程名匹配。比如pkill -f test.jar能杀掉启动命令行里包含 test.jar 的进程。pkill 默认发的也是 SIGTERM,真的要强杀再加-9。
这里有一个细节容易翻车:pkill 和 pgrep 在-f模式下匹配的是完整命令行,在不加-f时匹配的是进程名称,而进程名会被内核截断为 15 个字符。也就是说,如果进程名刚好超过 15 个字符,用普通模式可能匹配不到。批量操作前,我习惯先用pgrep确认匹配到的 PID 列表,再决定要不要执行 pkill,防止一次误杀,事后只能去翻备份恢复。
2.4 nice 与 renice:调整进程优先级
Linux 的 CPU 调度器支持进程优先级调整,nice 值的范围是 -20 到 19,-20 优先级最高,19 最低。普通用户只能把 nice 值调高(也就是降低自身进程的优先级),只有 root 能调低。启动时用nice -n 5 ./script.sh给任务降优先级,进程已经在跑了就用renice:
renice -n 10 -p <PID>实际使用场景很典型:某台服务器上跑着一个批处理任务,会持续大量占用 CPU,同时机器上还跑着对延迟敏感的在线服务。把批处理任务的 nice 值调到 10 以上,能明显减少它对在线服务的调度干扰。不过要提醒一句,nice 只影响 CPU 调度优先级,不影响内存占用和磁盘 IO 带宽。想限制内存或 IO,需要借助 cgroup 或者 ionice 这类机制。
3. 用户与权限管理:useradd 系列命令的正确姿势
3.1 useradd 的默认值陷阱:别指望发行版帮你收尾
新建用户最常见的错误,就是把useradd username当成一条"创建用户"的完整命令。实际上,在很多发行版上,这个命令默认不会创建用户的家目录,也不会复制/etc/skel里的初始配置文件。你创建一个普通用户,然后他登录进去发现连~/.bashrc都没有,遇到问题都不知道去哪查。
正确的打开方式是显式指定参数,长这样:
useradd -m -s /bin/bash -d /home/username -G devgroup username逐个拆解一下:
-m:创建家目录-s /bin/bash:指定登录 Shell,如果设定为/sbin/nologin,用户无法交互式登录,适合用来跑服务-d:指定家目录路径-G:把用户加入附加组,注意大写 G;小写-g指定的是主组
创建完用户后,还要执行passwd username设置初始密码,并让用户首次登录后自行修改。多数发行版默认启用了密码必改机制,但有些精简安装环境没有,这个细节不确认清楚,后面会引发安全问题。
顺带提一句 Debian 系上的adduser和useradd的区别:adduser是一个封装得更友好的脚本,会交互式地询问密码、创建家目录、复制配置,适合新手;useradd是底层工具,适合脚本化批量操作。两个都掌握,按场景选就行。
3.2 三个核心文件:passwd、shadow、group 的字段解读
用户管理绕不开三个配置文件,理解了它们,很多权限问题就不再是玄学。
/etc/passwd每一行代表一个用户,用冒号分隔成 7 个字段。依次是:用户名、密码占位符(真正的密码不在这里)、UID、GID、注释信息、家目录路径、登录 Shell。看到 UID 为 0 的用户就是超级用户,审计时第一件事就应该检查有没有非预期的 UID 0 账号。
/etc/shadow存放加密后的密码和密码策略,普通用户不可读。每个字段包括加密后的 Hash、最近一次修改密码的日期(从 1970-01-01 起的天数)、密码最短使用天数、最长有效期、过期前警告天数等。
/etc/group记录用户组信息,字段是组名、组密码占位、GID、组成员列表。我见过不少人手动编辑这三个文件来管理用户,然后因为语法错误把系统搞到无法登录。除非特殊情况,一律用 useradd、usermod、userdel、groupadd 这些命令来改,命令会在改文件前做语法校验,不会让你把自己锁在门外。
3.3 passwd 与 chage:密码策略不只是设个密码
passwd命令除了修改密码,配合参数可以做不少事。比如passwd -l username能锁定用户账号,passwd -u username解锁,passwd -e username强制用户在下次登录时修改密码。
密码过期策略则用chage管理。一个常见的需求:员工临时借调,账号只能用 90 天,到期自动失效。可以这样配置:
chage -M 90 -m 7 -W 10 username-M 90表示密码最长 90 天必须更换,-m 7表示两次修改密码之间至少间隔 7 天,-W 10表示过期前 10 天开始警告。如果想让账号在指定日期后彻底失效,用chage -E 2026-12-31 username。这些策略虽然直接写进 shadow 文件,但通过 chage 修改最稳妥。
3.4 sudo 的正确打开方式:别直接动 sudoers
需要给用户提权权限时,千万别想当然地去vim /etc/sudoers。这个文件语法很挑剔,一个笔误就可能让所有用户都无法提权。正确改法是用visudo命令,它会在保存前做语法校验,出错了还能选择不保存。
最简单的提权方式是给用户添加 wheel 组(CentOS/RHEL 系是 wheel,Debian/Ubuntu 系是 sudo 组):
usermod -aG wheel username注意-aG的 a 表示追加,不加 a 的话,-G会把用户从原来的附加组里全部移除,造成不必要的权限变化。给用户提权后,建议用sudo -l让他自己查看当前账号拥有的权限,也方便管理员确认配置生效。更细粒度的权限控制,可以在 sudoers 里限制到具体命令,比如只允许执行系统更新和重启服务,这个后面可以根据实际场景再加。
3.5 用户清理与权限回收:离职账号不是删了就行
清理一个不再使用的账号,最常用的命令是:
userdel -r username-r参数会同时删除用户的家目录和邮件目录。但实际运维中,光删用户还不够。我见过好几次这种情况:用户被删了,但定时任务还留在 crontab 里,服务配置里还引用着他的家目录路径,项目代码的属主变成了一串 UID 数字。正确的清理流程应该是:
- 先检查并移除用户的 crontab:
crontab -u username -l确认后清理 - 检查系统服务单元和
/etc/sudoers里是否还有引用 - 再用
userdel -r删除账号和家目录 - 最后
find / -nouser -o -nogroup找出无主文件,重新归属给其他用户
这个流程走完后,才算真的把权限回收干净了。
4. 系统资源观测:free、df、du、iostat 的真正读法
4.1 free 命令中的 buff/cache 不是"已用内存"
查看内存,几乎所有人都会用free -h。但很多新手一看到 buff/cache 占用几个 G,就开始慌,想着要不要手动清理。这个观念得纠正。buff/cache 是 Linux 的文件页缓存,用来加速磁盘读取的。当应用程序需要更多内存时,内核会自动回收这部分缓存,不需要你干预。
真正需要关注的是available这一列,它表示在不触发 swap 的情况下,系统还有多少内存可供新进程使用。判断内存是否吃紧,看available比看free列准确得多。比如一条free -h的输出中,free列只剩 300M,但available还有 3G,说明系统状态其实很健康。
那么什么时候需要担心?当available持续走低,同时si、so两个 swap 指标一直在跳动,说明系统已经开始频繁换页,性能会明显下降。这时候优先排查谁在消耗内存,用ps aux --sort=-%mem | head -10看排名,而不是急着加内存条。
4.2 磁盘满不等于空间满:inode 耗尽与 df -i
df -h查看磁盘空间是基础操作,但还有一个隐藏杀手是 inode 耗尽。inode 是文件系统里存储文件元信息的结构,每个文件或目录都要占用一个。当磁盘空间还剩不少,但df -i显示 inode 使用率 100% 时,系统会报"磁盘已满"或者"无法创建文件",实际上空间是有的,只是文件数量到了上限。
典型的元凶是小文件风暴。比如某个应用日志没有做按大小切割,每分钟生成几千个几 KB 的小文件,几天时间就能把 inode 耗尽。排查方法很简单:
df -i find / -xdev -type f | wc -l定位到对应目录后,重点检查是不是有无限生成小文件的任务。这种问题靠删大文件没用,只能清理小文件并加上日志轮转策略。
4.3 du 找大文件:从根目录逐层筛下去
磁盘空间告警时,du是你的定位工具。最常用的参数组合是:
du -h --max-depth=1 / | sort -hr | head -20它能把根目录下一级的所有子目录按空间占用排个序,从最大的开始筛。一层层往下钻,很快就能定位到是哪个目录爆了。du的这种读法比tree直观得多,也比一个个ls -lh高效得多。
还有个技巧:du -sh只看当前目录占用的总大小,lsof | grep deleted可以查出已经被删除但仍被进程占用的文件。线上经常出现一种情况:df 显示磁盘还有空间,但应用报写入失败,因为某个删掉的日志文件还被进程持有句柄,空间没有真正释放。找到持有句柄的进程并重启它,空间才会回来。
4.4 vmstat 与 iostat:一分钟定位瓶颈在 CPU 还是 IO
vmstat 1可以每秒刷新一次系统整体状态,输出列较多,关键的几个是:r(运行队列中的进程数)、us(用户态 CPU)、sy(内核态 CPU)、wa(等待 IO 的时间)、si/so(swap 换入换出)。如果wa长期偏高,说明磁盘 IO 吃紧;如果r远大于 CPU 核数,说明计算资源不够,进程都在排队。
iostat则更聚焦磁盘,重点看%util(设备忙绿程度)和await(IO 请求平均处理时间)。SSD 的%util到 100% 是很常见的高负载表现,但 HDD 到 60% 以上就要留意了。不过%util高并不绝对代表性能差,还要结合 IOPS 和await延迟一起判断,具体取决于底层磁盘类型和阵列配置。
在实际排障中,我会先用vmstat判断瓶颈大类,再用iostat细化到具体磁盘设备,最后配合进程级别的查看确认是哪个进程在发起大量读写。这三步走完,大部分性能问题的方向就八九不离十了。
5. systemctl 与现代服务管理
5.1 systemctl 的常用组合:一条命令完成启停与开机自启
现在的主流发行版都使用 systemd 作为服务管理器,对应命令就是 systemctl。日常管理服务,最常用的操作如下:
| 操作 | 命令 |
|---|---|
| 启动服务 | systemctl start nginx |
| 停止服务 | systemctl stop nginx |
| 重启服务 | systemctl restart nginx |
| 重新加载配置(不中断服务) | systemctl reload nginx |
| 设置开机自启 | systemctl enable nginx |
| 取消开机自启 | systemctl disable nginx |
| 查看运行状态 | systemctl status nginx |
很多人会分步执行 start 和 enable,其实可以直接写成systemctl enable --now nginx,一条命令同时完成开启自启和立即启动,省事也避免遗漏。查看状态时,也可以结合systemctl is-active和systemctl is-enabled快速得到脚本友好的输出,方便写监控脚本。
还有一个经常被忽略的操作:某个服务改完配置文件后,需要重新加载配置。这时候优先用reload,而不是restart,因为 reload 能做到热加载,对在线连接的影响小得多。如果服务不支持 reload,再退回 restart。
5.2 自己写一个 service 单元:把脚本变成受管服务
如果有一个脚本需要在后台以守护进程方式跑,与其用nohup挂在终端,不如写一个 systemd 服务单元文件,让它享受开机自启、崩溃自动重启、日志统一收集这些能力。一个最小可用的服务单元长这样:
[Unit] Description=My Custom Worker Service After=network.target [Service] Type=simple WorkingDirectory=/opt/myworker ExecStart=/usr/bin/python3 /opt/myworker/worker.py Restart=always RestartSec=5 User=workeruser [Install] WantedBy=multi-user.target把这个文件保存到/etc/systemd/system/myworker.service,然后依次执行:
systemctl daemon-reload systemctl enable --now myworker几个字段的用意要说清楚:Restart=always表示进程无论什么原因退出都会自动拉起,搭配RestartSec=5防止频繁崩溃时疯狂重启;User=workeruser指定进程运行身份,千万不要让服务以 root 跑,哪怕脚本没有特殊权限需求,也应该有个专用账号,风险小很多;WorkingDirectory指定工作目录,防止脚本里使用相对路径时找不到文件。
在开发环境调服务单元时,我踩过一个坑:改完 ExecStart 忘记执行systemctl daemon-reload,导致每次重启都是旧命令。记住,只要是编辑过 unit 文件,必须先执行 daemon-reload,否则 systemd 不会感知配置变更。
5.3 journalctl 日志排查:从服务日志到内核日志一把梭
systemd 把服务日志和系统日志统一收到 journal 里,查看工具就是journalctl。最常用的几个用法:
# 查看某个服务的全部日志 journalctl -u myworker # 只看最近的 50 行 journalctl -u myworker -n 50 # 持续跟踪日志输出 journalctl -u myworker -f # 按时间过滤 journalctl -u myworker --since "1 hour ago" # 查看上次启动到现在的系统日志 journalctl -b排障的时候,我喜欢把服务重启前后的日志放在一起对比。一般先systemctl status myworker看服务当前状态和最近日志,如果信息不够,再journalctl -u myworker -n 200拉历史。如果问题发生在内核层面,比如硬件错误、内存不足,那就要用dmesg或者journalctl -k来看内核日志。
有一个容易忽视的点:journal 日志默认会持续累积,占用的磁盘空间可能超过预期。时间长了,需要定期清理,用journalctl --vacuum-time=7d只保留最近 7 天的日志,这个操作在日志量大的服务器上应该纳入例行维护。
6. 故障排查的黄金习惯:日志、定时任务与内核参数
6.1 dmesg 与内核日志:硬件和内存问题的第一现场
当服务器出现原因不明的崩溃、挂起、或者某个进程神秘消失时,第一站应该是dmesg。它记录的是内核报告的消息,硬件故障、磁盘错误、内存不足、进程被 OOM Killer 处决,这些信息都在里面。
排查 OOM 的典型路径是:
dmesg | grep -i oom如果看到某个进程被 Out of memory 杀掉,再配合free -h确认当时内存是否紧张。还有一种情况,dmesg里频繁出现 I/O error 或者硬盘扇区报错,说明磁盘可能在物理层面出问题了,这时候应该尽快备份数据,联系更换硬件,而不是等着系统自己恢复。
我习惯在故障发生后第一时间先存一份dmesg -T的输出到文件,-T会把时间戳显示成人可读的格式。等排查结束再回头看,往往能发现最初忽略的端倪。这个习惯帮我在好几次故障复盘里找到了真正的根因,而不是停留在表面症状。
6.2 crontab 的坑:环境变量与日志重定向
定时任务管理是系统管理的日常操作,crontab -e编辑当前用户的计划任务,crontab -l查看已有任务,crontab -r清空所有任务。但 crontab 有几个隐藏的坑,几乎每个用 Linux 的人都会踩一遍。
最大的坑是环境变量。crontab 执行任务的环境非常精简,PATH 只有/usr/bin:/bin,很多命令的路径不在里面,脚本里如果用了python3、docker这类工具,直接执行会报 command not found。解决办法有两个:一是在脚本里写明绝对路径,二是在 crontab 文件开头设置变量,比如:
PATH=/usr/local/bin:/usr/bin:/bin第二个坑是日志输出。cron 默认会把标准输出和错误发邮件给用户,但邮件功能在很多服务器上并没有配置,导致任务跑挂了你也收不到通知。更可靠的做法是手动重定向:
0 2 * * * /opt/backup/backup.sh >> /var/log/backup.log 2>&1这样日志落盘,出了问事直接tail -f /var/log/backup.log就能看。排查别人的定时任务没执行时,先看/var/log/cron(部分发行版是/var/log/syslog),那里记录了 cron 服务的实际执行情况,比猜快得多。
6.3 sysctl 内核参数:改完要持久化
内核参数调整是高级系统管理的常见操作,查看当前参数用sysctl -a,临时修改用sysctl -w,比如:
sysctl -w vm.swappiness=10vm.swappiness控制系统使用 swap 的倾向,默认值在部分系统上偏高,对服务器负载表现有影响。参数含义是:值越低,越倾向于保持内存中的文件页缓存,减少换页。
但要注意,sysctl -w的修改只对当前运行生效,重启后失效。要持久化,得写进/etc/sysctl.conf或者/etc/sysctl.d/目录下新建的 conf 文件。改完之后执行sysctl -p重新加载,并立即检查有没有报错。修改内核参数前,强烈建议先确认参数的默认行为和可能影响,像是net.ipv4.ip_forward、kernel.core_pattern这类参数,改错了会影响整个系统的网络或崩溃转储行为。
6.4 沉淀几个长期受用的系统管理习惯
最后分享几个我长期坚持的习惯,它们看起来不起眼,但每次都能在关键时刻省下大把时间。
第一个是给高频命令做别名。把ps aux --sort=-%cpu | head、df -h、free -h这些组合写进~/.bashrc,日常查看变成敲两个字母的事。对团队常见的敏感命令,恢复性操作一律加上确认机制。
第二个是给所有手动操作留痕。修改核心系统文件之前,先备份一份带时间戳的副本,比如cp /etc/sysctl.conf /etc/sysctl.conf.bak.20250115。改完异常了,回滚就是一两条命令的事。
第三个是把握"恢复优先于诊断"的原则。故障现场第一时间不是分析为什么坏,而是先恢复服务。排查根因可以后面慢慢做,但业务停滞的每一分钟都在损失。这个原则我吃过几次亏才算真正领会。
系统管理命令看着琐碎,但每一条背后都对应着一个设计精良的系统机制。把这层机制搞明白,日常维护和应急排障就不是死记硬背,而是顺理成章的事。