第十八章 Linux故障排查与恢复

第18章 Linux故障排查与恢复

写在前面的话

想象一下,你是一个城市的"消防队长"。城市里每天都有大大小小的"火灾"------服务器突然卡死了、网站打不开了、磁盘满了报错了......你的任务就是在最短的时间内找到火源、扑灭大火、恢复秩序。这就是Linux故障排查与恢复的核心使命。

这一章是整本书的"压轴大戏"。前面17章你学了怎么用Linux、怎么配置服务、怎么写脚本,而现在,你要学的是------当一切都不工作的时候,你该怎么办。这就像学开车一样,前面学的是怎么踩油门、怎么打方向盘,而这一章学的是------当车子在高速上突然爆胎了,你该怎么冷静应对。

本章面向零基础小白,我们会用大量的生活类比(医生诊断、消防救火、侦探破案等)来讲解每一个概念,确保你看得懂、学得会、用得上。每一个命令都会给出完整示例,每一个案例都来自真实的运维场景。

本章学习目标:

  • 掌握系统化的故障排查方法论
  • 能够独立诊断和修复系统启动失败
  • 学会修复损坏的文件系统
  • 熟练排查网络故障
  • 诊断和解决性能瓶颈问题
  • 处理内存泄漏、磁盘满、僵尸进程等常见问题
  • 掌握日志分析技巧
  • 熟练使用单用户模式和救援模式
  • 制定数据备份与恢复策略
  • 组织灾难恢复演练

18.1 故障排查方法论

18.1.1 什么是故障排查?------用医生看病来类比

你有没有去医院看病的经历?当你身体不舒服去看医生时,医生会怎么做?

  1. 先问症状:"哪里不舒服?什么时候开始的?疼了多久?"
  2. 再查体征:量体温、测血压、听心跳
  3. 做检查:验血、拍X光、做CT
  4. 下诊断:根据检查结果判断是什么病
  5. 开药方:针对诊断结果给出治疗方案
  6. 观察疗效:吃药后看看有没有好转,没好转就换方案

Linux故障排查的过程和医生看病简直一模一样!只不过"病人"换成了你的Linux系统,"医生"就是你自己。

医生看病 Linux故障排查
问症状:哪里不舒服? 收集故障现象:什么报错?什么时候开始的?
量体温、测血压 查看系统资源:CPU、内存、磁盘使用率
验血、拍X光 查看日志文件、运行诊断命令
下诊断 定位根本原因
开药方 执行修复操作
观察疗效 验证故障是否消除

18.1.2 故障排查的黄金法则

在正式学习具体技巧之前,你必须牢记以下几条"黄金法则"。这些法则是无数运维工程师用血泪换来的经验,就像消防员进入火场前必须检查装备一样重要。

法则一:先冷静,再动手

当生产服务器出故障时,你的第一反应可能是慌张。但慌张是排障最大的敌人。就像消防员面对大火一样,越慌越容易出错。深呼吸三次,告诉自己:"Linux几乎所有的故障都是有迹可循的,我一定能找到原因。"

法则二:先收集信息,再下结论

很多新手一看到报错就急于修改,结果越改越乱。正确做法是先把故障信息收集完整:

  • 报错信息是什么?(截屏、复制完整报错)
  • 故障什么时候开始的?(精确到时间点)
  • 影响范围多大?(一台机器还是多台?)
  • 最近做过什么变更?(安装了什么?修改了什么配置?)
bash 复制代码
# 查看系统最近的重启和关机记录
last reboot | head -20
last -x | head -30

# 查看系统启动时间,判断是否最近重启过
uptime
who -b

# 查看最近的登录记录,看有没有人操作过
last | head -20

法则三:一次只改一个地方

这是极其重要的一条规则!如果你同时改了三个地方,故障消失了,你根本不知道是哪个改动起了作用。如果故障变得更严重了,你也不知道是哪个改动导致的。所以,每次只做一个修改,然后观察效果。

法则四:先备份,再修改

在修改任何配置文件之前,先备份!这就像做手术前要先签知情同意书一样。万一改错了,你还能恢复回来。

bash 复制代码
# 修改前先备份(加上日期标记)
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak.$(date +%Y%m%d)

# 更好的做法:用版本控制管理配置文件
cd /etc/nginx
git init
git add -A
git commit -m "修改前的备份状态"

法则五:从最可能的原因开始排查

医生看病不会一上来就让你做全身CT,而是先从最常见的病开始排除。Linux故障排查也一样,先检查最常见的故障原因,再逐步深入。

18.1.3 故障排查的系统化流程------OSI模型思维

我们可以把故障排查想象成"剥洋葱",从外到内一层一层地排查。这里介绍一个非常实用的"分层排查法":

第一层:物理层(硬件是否正常?)

  • 服务器电源是否正常?
  • 网线是否插好?
  • 硬盘指示灯是否正常闪烁?

第二层:系统层(操作系统是否正常?)

  • 系统能正常启动吗?
  • 内核有没有报错?
  • 文件系统是否正常?

第三层:网络层(网络是否通畅?)

  • IP地址配置正确吗?
  • 能ping通网关吗?
  • DNS解析正常吗?

第四层:应用层(服务是否正常?)

  • 服务进程是否在运行?
  • 端口是否在监听?
  • 配置文件是否正确?

第五层:用户层(用户体验如何?)

  • 用户能正常访问吗?
  • 响应速度是否正常?
  • 有没有报错页面?

让我们用一个实际例子来演示这个流程:

bash 复制代码
# ===== 第一层:物理层检查 =====
# 查看硬件信息
dmesg | grep -i error
dmesg | grep -i "hardware"

# 查看磁盘健康状态(需要smartmontools)
smartctl -a /dev/sda

# ===== 第二层:系统层检查 =====
# 查看系统负载
uptime
# 输出示例: 10:30:01 up 30 days, 5:20, 3 users, load average: 0.20, 0.18, 0.15

# 查看内存使用情况
free -h

# 查看磁盘使用情况
df -h

# ===== 第三层:网络层检查 =====
# 查看IP地址
ip addr show

# 测试网络连通性
ping -c 4 8.8.8.8

# 测试DNS解析
nslookup www.baidu.com

# ===== 第四层:应用层检查 =====
# 查看服务状态
systemctl status nginx

# 查看端口监听
ss -tlnp | grep :80

# 查看服务日志
journalctl -u nginx --since "1 hour ago"

# ===== 第五层:用户层检查 =====
# 模拟用户访问
curl -I http://localhost

18.1.4 常见故障排查误区

误区一:"重启能解决90%的问题"

很多人开玩笑说"重启大法好"。确实,重启有时能暂时解决问题,但它掩盖了真正的故障原因。就像你头疼吃止疼药,疼是不疼了,但病还在。在排查阶段,尽量不要急着重启,先把现场信息收集好。

bash 复制代码
# 在重启前,先收集诊断信息
# 保存当前进程快照
ps aux > /tmp/ps_snapshot_$(date +%Y%m%d_%H%M%S).txt

# 保存当前网络连接
ss -tunap > /tmp/ss_snapshot_$(date +%Y%m%d_%H%M%S).txt

# 保存当前内存状态
free -h > /tmp/memory_snapshot_$(date +%Y%m%d_%H%M%S).txt

# 保存dmesg输出
dmesg > /tmp/dmesg_snapshot_$(date +%Y%m%d_%H%M%S).txt

误区二:盲目搜索引擎

遇到问题直接Google或问AI当然可以,但如果你连故障现象都没搞清楚,搜索到的答案可能南辕北辙。正确做法是:先搞清楚症状,提取关键词,再搜索。

误区三:忽视日志

日志是Linux系统留给你的"遗言"------出了问题,系统在日志里几乎都留下了线索。不查日志就排查故障,就像侦探不看现场证据就破案一样荒唐。

18.1.5 建立故障排查文档

每排障一次,都要记录下来。这就像医生的病历本,记录得越详细,以后遇到类似问题就越快解决。建议每个运维人员都维护一个"故障案例库":

复制代码
故障案例记录模板:
==============================
故障编号:#2024-001
故障时间:2024-01-15 10:30
故障现象:Nginx网站无法访问
影响范围:所有用户
故障级别:P1(严重)
排查过程:
  1. 检查nginx服务状态 -> 正在运行
  2. 检查端口监听 -> 80端口正常监听
  3. 检查防火墙 -> iptables规则异常,80端口被禁止
根本原因:防火墙规则被误修改
解决方案:恢复防火墙规则,开放80端口
经验总结:修改防火墙前必须备份,修改后必须验证
==============================

18.1.6 小结

故障排查方法论是整章的基础。记住核心要点:

  1. 像医生看病一样系统化排查
  2. 先收集信息再动手
  3. 一次只改一个地方
  4. 先备份再修改
  5. 从最可能的原因开始排查
  6. 分层排查(物理→系统→网络→应用→用户)
  7. 重视日志,建立故障案例库

掌握了这套方法论,你就有了"渔"的能力,后面的具体技术就是"鱼"了。

18.1.7 故障排查的常用工具箱

就像消防员有各种专业的救火工具一样,Linux运维工程师也需要掌握一套"工具箱"。下面介绍几个最常用的排障工具,你不需要一次全部记住,但要知道在什么场景下用什么工具。

bash 复制代码
# ===== 系统信息类工具 =====

# uname - 查看内核版本和系统架构
uname -a
# Linux myserver 3.10.0-1160.el7.x86_64 #1 SMP ... x86_64 GNU/Linux

# hostnamectl - 查看主机详细信息(systemd系统)
hostnamectl
# Operating System: CentOS Linux 7 (Core)
# Kernel: Linux 3.10.0-1160.el7.x86_64
# Architecture: x86-64

# lscpu - 查看CPU信息
lscpu
# Architecture:        x86_64
# CPU(s):              4              <-- 4个CPU核心
# Model name:          Intel(R) Xeon(R) CPU E5-2680

# lsof - 列出打开的文件(Linux中一切皆文件,所以这个工具极其强大)
lsof -i :80           # 查看谁在使用80端口
lsof -i tcp           # 查看所有TCP连接
lsof -u mysql         # 查看mysql用户打开的文件
lsof /var/log/messages # 查看谁在写日志文件
lsof +D /tmp          # 查看谁在使用/tmp目录下的文件

# strace - 追踪进程的系统调用(排障终极武器)
# 当程序行为异常但不知道原因时,strace可以看到程序到底在做什么
strace -p 1234        # 追踪正在运行的进程
strace -e trace=network ./program  # 只追踪网络相关的系统调用
strace -e trace=file ./program      # 只追踪文件相关的系统调用

# ===== 综合诊断脚本 =====
# Linux系统自带了一个综合诊断脚本,可以一键收集系统信息
# 部分系统可能需要安装:yum install sos 或 apt install sosreport
sosreport
# 会生成一个打包的诊断报告,在联系厂商技术支持时经常需要

18.1.8 故障排查的心态修炼

技术可以学,但心态需要修炼。一个优秀的运维工程师在面对故障时,心态就像经验丰富的外科医生------冷静、专注、有条不紊。

第一层心态:不要恐慌

生产环境出故障时,几十个用户在等着,老板在旁边看着,你手心冒汗------这是正常的。但恐慌只会让你犯错。记住:任何故障都有原因,任何原因都有解决方案。深呼吸,然后开始系统化排查。

第二层心态:不要急于甩锅

"这不是我的问题,是开发的问题"、"这是网络的问题"、"这是硬件的问题"------甩锅不会让故障消失。先解决问题,再追究责任。故障恢复永远是第一优先级。

第三层心态:不要隐瞒

如果你操作失误导致了故障,不要试图隐瞒。坦诚地告诉团队发生了什么,大家一起解决。隐瞒只会让问题更严重,失去团队的信任。

第四层心态:事后复盘

故障解决后,一定要做复盘(Post-Mortem)。不是追究谁的责任,而是找出:

  • 故障是怎么发生的?
  • 为什么没有更早发现?
  • 怎么防止再次发生?
  • 排障过程中哪些地方可以改进?

每一次故障都是一次学习的机会。善于复盘的工程师,会越来越强。


18.2 系统无法启动排查

18.2.1 Linux启动过程详解------用"早起上班"来类比

要排查启动故障,你必须先了解Linux是怎么启动的。让我们用"早起上班"的过程来类比Linux的启动流程:

复制代码
BIOS/UEFI阶段  →  你被闹钟叫醒了
    ↓
GRUB阶段       →  你在考虑今天穿什么衣服、走哪条路
    ↓
内核加载阶段    →  你出门开始上班了
    ↓
init/systemd   →  你到了公司,开始安排今天的工作
    ↓
服务启动        →  各项工作开始执行
    ↓
登录界面        →  工作就绪,可以开始干活了

第一步:BIOS/UEFI(闹钟响了)

当你按下电源键,电脑主板上有一块小芯片叫BIOS(或新的UEFI),它就像你的闹钟。BIOS做的第一件事是"自检"(POST, Power-On Self Test),检查CPU、内存、显卡等硬件是否正常。这就像你醒来后先揉揉眼睛、伸个懒腰,确认自己身体没毛病。

然后BIOS按照设定的顺序去找启动盘------先看看U盘能不能启动,再看看硬盘,这叫"启动顺序"(Boot Order)。

第二步:GRUB(选择穿什么衣服)

BIOS找到启动盘后,会把控制权交给GRUB(GRand Unified Bootloader)。GRUB就是那个让你选择"启动哪个系统"的菜单。你装过双系统的话,肯定见过这个菜单。

GRUB做的事情:

  1. 读取自己的配置文件 /boot/grub/grub.cfg(或 /boot/grub2/grub.cfg
  2. 显示启动菜单(如果配置了的话)
  3. 根据你的选择,加载对应的内核

第三步:内核加载(出门上班)

GRUB加载内核(/boot/vmlinuz-xxx)和初始化内存盘(/boot/initramfs-xxx)到内存中。内核开始接管系统,就像你真正出门开始上班了。

内核做的事情:

  1. 初始化硬件驱动
  2. 挂载根文件系统(root filesystem)
  3. 启动第一个进程------init(或systemd),PID为1

第四步:systemd/init(安排今天的工作)

systemd是现代Linux的"大管家"(CentOS 7+、Ubuntu 16.04+都使用systemd)。它就像公司的部门经理,负责按顺序启动各种服务。

bash 复制代码
# 查看systemd的启动过程
systemd-analyze
# 输出示例:Startup finished in 3.210s (kernel) + 12.530s (userspace) = 15.740s

# 查看每个服务的启动耗时
systemd-analyze blame | head -20

# 查看启动时的关键路径(哪些服务是串行启动的)
systemd-analyze critical-chain

第五步:登录界面(准备就绪)

所有服务启动完毕后,系统显示登录界面,你就可以输入用户名密码登录了。

18.2.2 启动故障的分类与排查

了解了启动过程,我们就知道故障可能出现在哪个环节。就像早起上班,可能是闹钟没响、可能是衣服找不到、可能是路上堵车、也可能是到了公司发现钥匙丢了。

故障一:BIOS阶段故障------"闹钟没响"

现象:按下电源键后屏幕全黑,没有任何显示。

可能原因

  1. 电源问题(电源线没插好、电源坏了)
  2. 主板问题(BIOS电池没电了、主板坏了)
  3. 硬件故障(内存条松了、CPU故障)

排查方法

  • 听主板有没有"滴"一声------这是BIOS自检通过的声音
  • 听有没有连续的"滴滴"报警声------不同报警声代表不同硬件故障
  • 检查显示器连接是否正常

这类故障属于硬件层面,通常需要物理接触服务器,不在纯软件层面能解决的范围内。

故障二:GRUB阶段故障------"找不到路了"

现象 :屏幕显示 grub>grub rescue> 提示符,或者显示 GRUB loading... 后卡住。

这是GRUB找不到配置文件或内核文件了。就像你出门后发现导航坏了,不知道该怎么走了。

排查与修复------grub rescue模式

当看到 grub rescue> 提示符时,说明GRUB的基本程序还在,但找不到模块和配置文件了。这时候需要手动引导:

bash 复制代码
# 第一步:查看有哪些硬盘和分区
grub rescue> ls
# 输出示例:(hd0) (hd0,msdos1) (hd0,msdos2) (hd1) (hd1,msdos1)

# 第二步:逐个查看分区,找到根文件系统所在分区
grub rescue> ls (hd0,msdos1)/
# 如果输出"Filesystem is unknown",说明这个分区不是我们要找的
# 如果输出文件列表(bin boot dev etc ...),说明找到了!

# 第三步:设置根分区和前缀
grub rescue> set root=(hd0,msdos1)
grub rescue> set prefix=(hd0,msdos1)/boot/grub

# 第四步:加载normal模块
grub rescue> insmod normal

# 第五步:进入正常的GRUB菜单
grub rescue> normal

# 进入GRUB菜单后选择系统启动
# 启动成功后,重新安装GRUB
sudo grub-install /dev/sda
sudo update-grub

排查与修复------重新安装GRUB

有时候GRUB配置文件损坏,需要重新生成:

bash 复制代码
# 方法一:在运行的系统中重新生成GRUB配置
sudo grub2-mkconfig -o /boot/grub2/grub.cfg      # CentOS/RHEL
sudo update-grub                                   # Ubuntu/Debian

# 方法二:使用Live CD/USB启动后修复
# 1. 用U盘启动Live系统
# 2. 挂载根分区
sudo mount /dev/sda1 /mnt
# 3. 如果有独立的/boot分区,也要挂载
sudo mount /dev/sda2 /mnt/boot
# 4. 挂载虚拟文件系统
sudo mount --bind /dev /mnt/dev
sudo mount --bind /proc /mnt/proc
sudo mount --bind /sys /mnt/sys
# 5. chroot到挂载的系统
sudo chroot /mnt
# 6. 重新安装GRUB
grub-install /dev/sda
grub2-mkconfig -o /boot/grub2/grub.cfg
# 7. 退出并重启
exit
sudo reboot
故障三:内核加载阶段故障------"路上出事了"

现象 :内核加载过程中卡住,或者显示 Kernel Panic(内核恐慌)。

Kernel Panic就像你上班路上突然发生了车祸,彻底走不动了。内核遇到了无法恢复的致命错误,只能停下来。

常见原因

  1. 根文件系统找不到(最常见)
  2. 内核文件损坏
  3. 硬件不兼容
  4. 内核参数配置错误

排查------查看启动日志

在启动时按 e 键编辑GRUB条目,可以在内核参数行添加调试参数:

复制代码
# 在linux行末尾添加以下参数:
linux /boot/vmlinuz-xxx root=UUID=xxx ro quiet rd.debug systemd.log_level=debug
# 把quiet去掉,改成verbose,这样可以看到详细的启动信息

修复------根文件系统找不到

这是最常见的Kernel Panic原因。通常是因为:

  • 硬盘UUID变了
  • root参数配置错误
  • initramfs损坏
bash 复制代码
# 查看当前硬盘的UUID
blkid
# 输出示例:
# /dev/sda1: UUID="a1b2c3d4-..." TYPE="ext4"
# /dev/sda2: UUID="e5f6g7h8-..." TYPE="swap"

# 在GRUB中查看root参数是否正确
cat /boot/grub/grub.cfg | grep root

# 重建initramfs(在Live CD中操作)
# 挂载系统分区后
chroot /mnt
dracut --force        # CentOS/RHEL
update-initramfs -u   # Ubuntu/Debian
故障四:systemd阶段故障------"到了公司但打不开门"

现象:内核加载完成,但系统卡在某个服务启动的地方,无法进入登录界面。

排查方法

bash 复制代码
# 方法一:在启动时按e编辑GRUB,在linux行末尾加上:
systemd.unit=rescue.target
# 这样会进入救援模式(单用户模式),只启动基本服务

# 方法二:进入系统后查看哪些服务启动失败
systemctl --failed
# 输出示例:
#   UNIT            LOAD   ACTIVE SUB    DESCRIPTION
#   nginx.service   loaded failed failed The nginx HTTP and reverse proxy server

# 查看失败服务的详细日志
systemctl status nginx.service
journalctl -u nginx.service

实际案例:fstab配置错误导致无法启动

这是一个非常经典的故障。假设你修改了 /etc/fstab 文件,加了一条错误的挂载项,重启后系统就起不来了。系统会提示你进入紧急模式(emergency mode)。

bash 复制代码
# 系统提示进入emergency mode后,输入root密码进入命令行
# 然后编辑fstab文件
vi /etc/fstab

# 找到错误的行,注释掉或修正
# /dev/sdb1  /data  ext4  defaults  0 0   <-- 这行有问题
# 改成正确的设备名或注释掉
# /dev/sdb1  /data  ext4  defaults  0 0

# 保存后重启
reboot

18.2.3 启动故障排查的通用技巧

技巧一:活用GRUB编辑功能

启动时按 e 可以临时编辑启动项,这在排查启动问题时极其有用。修改后按 Ctrl+X 启动。注意,这种修改是临时的,不会永久保存。

技巧二:查看启动日志

bash 复制代码
# 查看本次启动的日志
journalctl -b

# 查看上一次启动的日志(上次启动失败时极其有用)
journalctl -b -1

# 查看启动过程中的错误
journalctl -b -p err

# 查看内核启动日志
dmesg | more

技巧三:制作应急启动U盘

永远要准备一个Live USB!这就像家里要备着急救箱一样。当系统完全无法启动时,Live USB是你最后的救命稻草。

bash 复制代码
# 在Linux中制作Live USB
# 先下载ISO镜像,然后:
sudo dd if=ubuntu-22.04.iso of=/dev/sdb bs=4M status=progress
# 注意:/dev/sdb是你的U盘设备名,千万别写错!
# 写错了会毁掉其他硬盘的数据!

# 在Windows中可以用Rufus工具制作

18.2.4 小结

系统启动故障是最让人紧张的故障之一,因为你连系统都进不去。但只要理解了启动流程,就能一步步定位问题所在:

  1. BIOS阶段------检查硬件
  2. GRUB阶段------修复GRUB配置
  3. 内核阶段------修复内核参数和initramfs
  4. systemd阶段------修复失败的服务和配置

记住:遇到启动故障不要慌,Live USB + chroot 是你的万能钥匙。


18.3 文件系统损坏修复

18.3.1 什么是文件系统?------用"图书馆"来类比

要理解文件系统损坏,我们先得理解什么是文件系统。

想象一下,你的硬盘是一个巨大的图书馆,里面存放着成千上万本书(文件)。如果这些书乱七八糟地堆在一起,你怎么找到你要的那本?你需要一个"图书管理系统"------记录每本书放在哪个书架、哪一层、哪个位置。

文件系统就是这个"图书管理系统"。它包括:

  • 超级块(Superblock):相当于图书馆的总目录,记录了整个文件系统的基本信息
  • inode(索引节点):相当于每本书的"图书卡片",记录了文件的属性和数据存放位置
  • 数据块(Data Block):相当于书架,实际存放文件数据的地方
  • 目录项(Dentry):相当于目录索引,把文件名和inode关联起来

常见的Linux文件系统类型:

文件系统 特点 适用场景
ext4 最稳定、最成熟 通用场景
xfs 大文件性能好 大容量存储
btrfs 支持快照、压缩 高级需求
zfs 数据完整性极强 企业存储

18.3.2 文件系统为什么会损坏?

文件系统损坏就像图书馆的目录被弄乱了------书还在,但你找不到它们了。常见原因有:

  1. 非正常关机:就像你在图书馆正在登记新书时突然停电了,登记到一半的信息就乱了
  2. 硬盘坏道:书架本身坏了,放上去的书被损坏了
  3. 内存故障:内存中的数据写错了,导致写入硬盘的数据也是错的
  4. 软件bug:某个程序错误操作导致文件系统结构被破坏
  5. 电源故障:和突然断电类似,写入操作被打断

18.3.3 检测文件系统状态

bash 复制代码
# 查看文件系统类型和挂载状态
df -Th
# 输出示例:
# Filesystem     Type      Size  Used Avail Use% Mounted on
# /dev/sda1      ext4       50G   20G   28G  42% /
# /dev/sda2      ext4      100G   60G   35G  64% /home

# 查看文件系统的详细信息
dumpe2fs /dev/sda1 | head -30   # ext4文件系统
xfs_info /dev/sda1               # xfs文件系统

# 检查文件系统是否有错误(只读检查,不修复)
# 注意:必须在卸载状态下检查!
umount /dev/sda1
e2fsck -n /dev/sda1    # ext4只读检查
# -n 参数表示不修改任何东西,只检查

18.3.4 使用fsck修复文件系统

fsck(File System Consistency Check)是修复文件系统的主力工具,就像图书馆的"整理员",负责把乱了的目录重新整理好。

基本用法

bash 复制代码
# 第一步:卸载文件系统(必须!)
umount /dev/sda1

# 第二步:运行fsck检查并修复
fsck /dev/sda1
# fsck会自动识别文件系统类型并选择对应的工具

# 如果是ext4文件系统,可以更精确地使用:
e2fsck /dev/sda1

# 如果是xfs文件系统:
xfs_repair /dev/sda1

fsck的常用参数

bash 复制代码
# -y:对所有问题自动回答"yes"(自动修复所有问题)
fsck -y /dev/sda1
# 小白提示:第一次用建议先不加-y,手动确认每个修复操作

# -f:强制检查(即使文件系统标记为clean也检查)
fsck -f /dev/sda1

# -v:显示详细信息
fsck -v /dev/sda1

# -c:检查坏块(会比较慢,但对硬盘健康检查很有用)
fsck -c /dev/sda1

fsck交互过程示例

复制代码
# fsck /dev/sda1
fsck from util-linux 2.37.2
e2fsck 1.46.5 (30-Dec-2021)
/dev/sda1 contains a file system with errors, check forced.
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
Block bitmap differences:  +12345 -23456
Fix<y>? y                        <-- 这里问你修不修,输入y
Free blocks count wrong (1234567, counted=1234555).
Fix<y>? y                        <-- 继续输入y
Free inodes count wrong (500000, counted=499998).
Fix<y>? y
/dev/sda1: ***** FILE SYSTEM WAS MODIFIED *****

18.3.5 修复超级块损坏------"图书馆总目录丢了"

超级块(Superblock)是文件系统的"总目录",如果它损坏了,整个文件系统都无法挂载。但聪明的Linux设计者早就想到了这个问题------他们在文件系统的不同位置保存了超级块的多个备份!这就像图书馆不仅有总目录,还在不同楼层放了备份目录。

bash 复制代码
# 现象:尝试挂载时报错
mount /dev/sda1 /mnt
# 报错:mount: /mnt: can't read superblock on /dev/sda1.

# 第一步:查看超级块备份的位置
mke2fs -n /dev/sda1
# 注意:-n参数表示不实际格式化,只显示信息!千万别忘了-n!
# 输出示例:
# Superblock backups stored on blocks:
#   32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632

# 第二步:使用备份超级块修复
e2fsck -b 32768 /dev/sda1
# -b后面跟的是备份超级块所在的块号

# 第三步:如果修复成功,用备份超级块挂载
mount -o sb=32768 /dev/sda1 /mnt

# 第四步:恢复主超级块
# 先卸载
umount /dev/sda1
# 再次运行fsck,让它用修复后的信息重建主超级块
e2fsck /dev/sda1

18.3.6 修复只读文件系统

有时候你会发现文件系统突然变成只读了,无法写入任何文件。这是文件系统发现错误后,为了保护数据而自动切换到"只读模式"。就像图书馆发现书籍可能被损坏了,立刻关闭借阅,只允许看不允许借。

bash 复制代码
# 现象:创建文件时报错
touch test.txt
# 报错:touch: cannot touch 'test.txt': Read-only file system

# 查看文件系统状态
mount | grep " / "
# /dev/sda1 on / type ext4 (ro,relatate)   <-- ro表示只读

# 第一步:尝试重新挂载为读写模式
mount -o remount,rw /

# 如果上面失败,说明文件系统确实有错误,需要修复
# 第二步:卸载并修复(如果根分区无法卸载,需要进入救援模式)
umount /dev/sda1
fsck -y /dev/sda1

# 第三步:重新挂载
mount /dev/sda1 /mnt

18.3.7 xfs文件系统的修复

xfs文件系统在CentOS 7+中是默认文件系统,它的修复方式和ext4有所不同:

bash 复制代码
# xfs修复前,如果可以挂载,先尝试用xfs_repair的只读模式检查
xfs_repair -n /dev/sda1
# -n表示no-op mode,只检查不修复

# 修复xfs文件系统
umount /dev/sda1
xfs_repair /dev/sda1

# 如果xfs_repair报错说log有脏数据,需要先清空日志
xfs_repair -L /dev/sda1
# 注意:-L会清空日志,可能导致最近的数据丢失,谨慎使用!

# 修复后如果xfs元数据损坏严重,可以重建元数据
# 这是最后的手段,会格式化元数据但保留数据
xfs_repair -L /dev/sda1

18.3.8 实际案例:服务器断电后无法启动

故障背景:一台运行MySQL数据库的服务器突然断电,恢复供电后重启,系统无法正常启动,卡在文件系统检查阶段。

排查过程

bash 复制代码
# 1. 用Live USB启动,打开终端

# 2. 查看硬盘分区
lsblk
# 输出:
# sda      8:0    0 500G  0 disk
# ├─sda1   8:1    0   1G  0 part  /boot
# ├─sda2   8:2    0  50G  0 part  /
# └─sda3   8:3    0 449G  0 part  /data

# 3. 尝试挂载根分区
mount /dev/sda2 /mnt
# 报错:mount: /mnt: wrong fs type, bad option, bad superblock

# 4. 检查文件系统
fsck /dev/sda2
# 发现超级块损坏

# 5. 查找备份超级块
mke2fs -n /dev/sda2

# 6. 用备份超级块修复
e2fsck -b 32768 /dev/sda2

# 7. 修复成功后挂载
mount /dev/sda2 /mnt

# 8. 同样检查数据分区
fsck /dev/sda3

# 9. chroot进去重装GRUB
mount --bind /dev /mnt/dev
mount --bind /proc /mnt/proc
mount --bind /sys /mnt/sys
chroot /mnt
grub-install /dev/sda
grub2-mkconfig -o /boot/grub2/grub.cfg

# 10. 退出重启
exit
reboot

# 11. 系统启动后,检查MySQL数据完整性
mysqlcheck --all-databases --check

18.3.9 预防文件系统损坏

预防胜于治疗,以下措施可以大幅降低文件系统损坏的概率:

bash 复制代码
# 1. 确保定期检查文件系统
# 编辑 /etc/fstab,确保最后一列(pass参数)设置正确
# /dev/sda1  /      ext4  defaults  1  1   <-- 根分区设为1,优先检查
# /dev/sda2  /home  ext4  defaults  0  2   <-- 其他分区设为2
# /dev/sda3  swap   swap  defaults  0  0   <-- swap分区设为0,不检查

# 2. 定期检查硬盘健康状态
smartctl -t short /dev/sda    # 短测试
smartctl -t long /dev/sda     # 长测试
smartctl -a /dev/sda          # 查看健康状态

# 3. 使用tune2fs设置自动检查周期
tune2fs -c 30 /dev/sda1   # 每30次挂载检查一次
tune2fs -i 180d /dev/sda1 # 每180天检查一次

# 4. 配置UPS不间断电源,避免突然断电
# 硬件层面的预防,这里不展开

# 5. 使用journaling文件系统(ext4/xfs默认已开启日志功能)
dumpe2fs /dev/sda1 | grep 'features'
# 确认输出中包含 has_journal

18.3.10 小结

文件系统损坏是Linux运维中的常见问题,核心知识点:

  1. 文件系统就像图书馆的管理系统,记录文件的存放位置
  2. 非正常关机是损坏的主因
  3. fsck是修复的主力工具,修复前必须卸载分区
  4. 超级块损坏可以用备份恢复
  5. xfs文件系统用xfs_repair修复
  6. 预防为主:定期检查、配置UPS、使用日志文件系统

18.4 网络故障排查

18.4.1 网络排查思路------用"寄快递"来类比

网络通信就像寄快递。你(客户端)要把一个包裹(数据包)寄给远方的朋友(服务器),中间要经过快递公司分拣中心(路由器)、不同城市的转运站(网关)。

如果包裹寄不到,可能是哪个环节出了问题?

  1. 你自己写错地址了 → IP地址配置错误
  2. 快递公司不收你的包裹 → 防火墙拦截
  3. 分拣中心不知道怎么转发 → 路由配置错误
  4. 收件人地址不存在 → 目标服务器没开或端口没监听
  5. 收件人不在家 → 服务没运行
  6. 包裹被弄丢了 → 网络丢包
  7. 快递太慢了 → 网络延迟高

18.4.2 网络排查的分层方法

和前面讲的一样,网络排查也要分层进行。我们按照TCP/IP模型从下到上排查:

第一层:链路层------网线插好了没?

bash 复制代码
# 查看网卡状态(Link detected: yes表示网线已连接)
ethtool eth0
# 关键输出:
#   Speed: 1000Mb/s        <-- 网速
#   Link detected: yes     <-- 网线是否连接

# 查看所有网络接口
ip link show
# 输出示例:
# 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...
# LOWER_UP表示物理链路已建立

# 如果网卡是down的状态,手动启用
ip link set eth0 up

第二层:网络层------IP配对了没?

bash 复制代码
# 查看IP地址
ip addr show eth0
# 输出示例:
# 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc ...
#     link/ether 00:0c:29:12:34:56 brd ff:ff:ff:ff:ff:ff
#     inet 192.168.1.100/24 brd 192.168.1.255 scope global eth0

# 查看路由表(网关配置)
ip route show
# 输出示例:
# default via 192.168.1.1 dev eth0     <-- 默认网关
# 192.168.1.0/24 dev eth0 proto kernel <-- 本网段路由

# 测试网关是否可达
ping -c 4 192.168.1.1

# 如果ping不通网关,可能是:
# 1. IP地址配错了(不在同一个网段)
# 2. 网关设备故障
# 3. 网卡驱动问题

第三层:传输层------端口通不通?

bash 复制代码
# 测试到目标服务器的TCP端口是否可达
# 使用telnet
telnet 192.168.1.200 80
# Trying 192.168.1.200...
# Connected to 192.168.1.200.    <-- 表示端口通
# Escape character is '^]'.

# 使用nc(netcat)更灵活
nc -zv 192.168.1.200 80
# Connection to 192.168.1.200 80 port [tcp/http] succeeded!

# 测试一个范围的端口
nc -zv 192.168.1.200 20-30

# 查看本机监听的端口
ss -tlnp
# 输出示例:
# State   Recv-Q  Send-Q  Local Address:Port  Peer Address:Port  Process
# LISTEN  0       128     0.0.0.0:22          0.0.0.0:*          users:(("sshd",pid=1234,fd=3))
# LISTEN  0       128     0.0.0.0:80          0.0.0.0:*          users:(("nginx",pid=5678,fd=6))

第四层:应用层------DNS解析对了没?

bash 复制代码
# 测试DNS解析
nslookup www.example.com
# 输出示例:
# Server:     8.8.8.8
# Address:    8.8.8.8#53
# Non-authoritative answer:
# Name:   www.example.com
# Address: 93.184.216.34

# 使用dig获取更详细的信息
dig www.example.com
# 关注 ANSWER SECTION 部分

# 如果DNS解析失败,尝试使用指定的DNS服务器
dig @8.8.8.8 www.example.com

# 查看当前配置的DNS服务器
cat /etc/resolv.conf
# nameserver 8.8.8.8
# nameserver 114.114.114.114

18.4.3 网络排查常用工具详解

ping------最基础的网络测试工具

ping就像你喊一声"有人吗?"然后等待回应。它使用ICMP协议,不涉及端口。

bash 复制代码
# 基本用法:发送4个包
ping -c 4 8.8.8.8
# 输出:
# PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
# 64 bytes from 8.8.8.8: icmp_seq=1 ttl=117 time=12.3 ms
# 64 bytes from 8.8.8.8: icmp_seq=2 ttl=117 time=12.1 ms
# 64 bytes from 8.8.8.8: icmp_seq=3 ttl=117 time=12.5 ms
# 64 bytes from 8.8.8.8: icmp_seq=4 ttl=117 time=12.2 ms
# 
# --- 8.8.8.8 ping statistics ---
# 4 packets transmitted, 4 received, 0% packet loss   <-- 0%丢包,网络好
# rtt min/avg/max/mdev = 12.123/12.285/12.521/0.156 ms

# 持续ping,直到手动停止(适合监控网络稳定性)
ping 8.8.8.8
# 按Ctrl+C停止

# 指定包的大小(测试大包是否被分片)
ping -c 4 -s 1400 8.8.8.8

# 快速ping(间隔100ms,适合大范围扫描)
ping -c 4 -i 0.1 8.8.8.8
traceroute/tracepath------追踪数据包路径

traceroute就像快递追踪------你寄出的包裹经过了哪些中转站,每个中转站花了多长时间。

bash 复制代码
# 追踪到目标的路径
traceroute www.baidu.com
# 输出示例:
# traceroute to www.baidu.com (110.242.68.66), 30 hops max, 60 byte packets
#  1  192.168.1.1 (192.168.1.1)  0.526 ms   <-- 第一跳:网关
#  2  10.0.0.1 (10.0.0.1)  5.231 ms         <-- 第二跳:ISP路由器
#  3  202.97.12.45 (202.97.12.45)  12.451 ms <-- 第三跳:骨干网
#  4  110.242.68.66 (110.242.68.66)  15.621 ms <-- 目标

# 如果某一跳显示 * * *,说明那一跳不响应或被防火墙拦截
# 如果在某一跳之后全部是 * * *,说明故障就在那一跳

# tracepath不需要root权限,更方便
tracepath www.baidu.com

# mtr结合了ping和traceroute的优点,实时显示每一跳的丢包率
mtr www.baidu.com
# 这个工具在排查网络质量问题时非常有用
tcpdump------网络抓包利器

tcpdump就像在网络线路上装了一个"窃听器",可以看到所有经过的数据包。这是网络排障的终极武器。

bash 复制代码
# 基本用法:抓取指定网卡的所有流量
tcpdump -i eth0

# 抓取指定主机的流量
tcpdump -i eth0 host 192.168.1.100

# 抓取指定端口的流量
tcpdump -i eth0 port 80

# 抓取指定主机和端口的流量
tcpdump -i eth0 host 192.168.1.100 and port 80

# 抓取TCP SYN包(连接建立请求)
tcpdump -i eth0 'tcp[tcpflags] & tcp-syn != 0'

# 保存抓包结果到文件(用Wireshark分析)
tcpdump -i eth0 -w /tmp/capture.pcap port 80

# 读取抓包文件
tcpdump -r /tmp/capture.pcap

# 显示详细的内容(包括数据内容)
tcpdump -i eth0 -X port 80

# 只抓前10个包
tcpdump -i eth0 -c 10 port 80
ss------比netstat更强大的网络状态查看工具
bash 复制代码
# 查看所有TCP连接
ss -t -a
# State   Recv-Q  Send-Q  Local Address:Port  Peer Address:Port
# LISTEN  0       128     0.0.0.0:ssh         0.0.0.0:*
# ESTAB   0       0       192.168.1.100:ssh   10.0.0.50:54321

# 查看监听的端口和对应进程
ss -tlnp

# 查看所有UDP连接
ss -u -a

# 查看已建立的TCP连接
ss -t state established

# 统计各状态的连接数(在排查连接数过多时有用)
ss -s
# Total: 150
# TCP:   30 (estab 5, closed 10, orphaned 0, timewait 10)

# 查看连接指定端口的客户端
ss -tn state established '( dport = :80 or sport = :80 )'

18.4.4 常见网络故障案例

案例一:服务器能ping通但无法访问Web服务

排查思路:能ping通说明网络层没问题,问题在传输层或应用层。

bash 复制代码
# 1. 检查服务是否在运行
systemctl status nginx
# 如果没运行,启动它
systemctl start nginx

# 2. 检查端口是否在监听
ss -tlnp | grep :80
# 如果没有输出,说明nginx虽然启动了但没有监听80端口
# 可能是配置文件有问题

# 3. 检查防火墙是否放行了80端口
# iptables防火墙
iptables -L -n | grep 80
# 如果没有放行
iptables -I INPUT -p tcp --dport 80 -j ACCEPT

# firewalld防火墙(CentOS 7+)
firewall-cmd --list-ports
firewall-cmd --add-port=80/tcp --permanent
firewall-cmd --reload

# ufw防火墙(Ubuntu)
ufw status
ufw allow 80/tcp

# 4. 检查SELinux是否阻止了(CentOS)
getenforce
# 如果返回Enforcing,临时关闭测试
setenforce 0
# 如果关闭后可以访问,说明是SELinux的问题
# 永久解决方案:设置正确的SELinux上下文
semanage port -a -t http_port_t -p tcp 80
案例二:DNS解析失败

现象:ping IP地址可以,但ping域名不行。

bash 复制代码
# 1. 测试DNS解析
nslookup www.baidu.com
# 如果报错:server can't find www.baidu.com: NXDOMAIN

# 2. 检查DNS配置
cat /etc/resolv.conf
# 如果为空或DNS服务器不对,添加正确的DNS
echo "nameserver 8.8.8.8" > /etc/resolv.conf
echo "nameserver 114.114.114.114" >> /etc/resolv.conf

# 3. 检查/etc/hosts文件是否覆盖了DNS
cat /etc/hosts
# 如果有错误的条目,删除或修正

# 4. 检查/etc/nsswitch.conf确保DNS查询顺序正确
cat /etc/nsswitch.conf | grep hosts
# 应该是:hosts: files dns  (先查hosts文件,再查DNS)

# 5. 清除DNS缓存(有些系统有nscd缓存)
systemctl restart nscd    # 如果安装了nscd
案例三:网络连接时断时续

现象:网络有时通有时不通,很不稳定。

bash 复制代码
# 1. 使用mtr持续监控网络质量
mtr -r -c 100 8.8.8.8
# -r表示报告模式,-c 100表示发100个包
# 观察哪一跳丢包率高

# 2. 检查网卡是否有错误
ip -s link show eth0
# 关注RX errors(接收错误)和TX errors(发送错误)

# 3. 检查网卡速率和双工模式是否匹配
ethtool eth0
# Speed: 1000Mb/s
# Duplex: Full
# 如果和交换机不匹配,可能导致丢包
# 手动设置:
ethtool -s eth0 speed 1000 duplex full autoneg off

# 4. 检查网络内核参数
sysctl net.core.netdev_max_backlog
# 如果队列太小,网络繁忙时可能丢包
sysctl -w net.core.netdev_max_backlog=5000

# 5. 检查ARP表是否有冲突
arp -n
# 如果同一个IP对应两个不同的MAC地址,说明有IP冲突

18.4.5 网络配置文件

了解配置文件位置,才能在命令行修复时知道改哪里:

bash 复制代码
# ===== CentOS/RHEL =====
# 网卡配置文件
cat /etc/sysconfig/network-scripts/ifcfg-eth0
# TYPE=Ethernet
# BOOTPROTO=static
# IPADDR=192.168.1.100
# NETMASK=255.255.255.0
# GATEWAY=192.168.1.1
# DNS1=8.8.8.8
# ONBOOT=yes

# 重启网络服务
systemctl restart network    # CentOS 6/7
nmcli connection reload      # CentOS 8+

# ===== Ubuntu/Debian =====
# 网卡配置文件(旧版)
cat /etc/network/interfaces

# 新版使用Netplan
cat /etc/netplan/01-netcfg.yaml
# network:
#   version: 2
#   ethernets:
#     eth0:
#       addresses: [192.168.1.100/24]
#       gateway4: 192.168.1.1
#       nameservers:
#         addresses: [8.8.8.8, 8.8.4.4]

# 应用Netplan配置
netplan apply

18.4.6 小结

网络故障排查是运维工作中最常见的任务。核心要点:

  1. 分层排查:链路层→网络层→传输层→应用层
  2. ping测试连通性,traceroute/mtr追踪路径
  3. ss查看连接状态,tcpdump抓包分析
  4. 别忘了检查防火墙和SELinux
  5. DNS问题要先排查 /etc/resolv.conf/etc/hosts

18.5 性能问题诊断

18.5.1 性能问题概述------用"交通堵塞"来类比

系统性能问题就像城市交通堵塞。一条路上有车(进程)、有红绿灯(CPU调度)、有停车场(内存)、有高速公路(磁盘I/O)。任何一个环节出问题都可能导致"堵车"(系统变慢)。

常见的性能瓶颈有四种:

  1. CPU瓶颈:路口红绿灯处理不过来,车排长队
  2. 内存瓶颈:停车场满了,车无处停只能等
  3. 磁盘I/O瓶颈:高速公路出入口太窄,车进出慢
  4. 网络瓶颈:道路太窄,车流量大时通行缓慢

18.5.2 CPU性能分析

top命令------系统监控的"瑞士军刀"

top是最常用的系统监控工具,就像汽车的仪表盘,一眼就能看到系统的整体状态。

bash 复制代码
top
# 输出解读:
# top - 10:30:01 up 30 days,  5:20,  3 users,  load average: 1.50, 1.20, 0.80
# Tasks: 150 total,   1 running, 149 sleeping,   0 stopped,   0 zombie
# %Cpu(s): 25.0 us,  10.0 sy,   0.0 ni, 63.0 id,   2.0 wa,   0.0 hi,   0.0 si,   0.0 st
# MiB Mem :   8000.0 total,   2000.0 free,   4000.0 used,   2000.0 buff/cache
# MiB Swap:   2000.0 total,   2000.0 free,      0.0 used.   3000.0 avail Mem
#
#   PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
#  1234 mysql     20   0 2000000 500000  20000 S  85.0  6.2   1200:00 mysqld
#  5678 nginx     20   0  150000  20000   5000 S  15.0  0.2     50:00 nginx

关键指标解读

指标 含义 类比
load average 系统负载(1/5/15分钟平均值) 等待结账的顾客数量
us 用户态CPU使用率 顾客在买东西
sy 内核态CPU使用率 收银员在处理系统操作
id 空闲CPU百分比 收银员闲着
wa I/O等待百分比 收银员在等仓库取货
VIRT 虚拟内存大小 商店的总面积
RES 物理内存使用量 实际使用的面积
SHR 共享内存 公共区域面积
bash 复制代码
# top的交互命令(在top界面中按键):
# P  - 按CPU使用率排序
# M  - 按内存使用率排序
# T  - 按运行时间排序
# 1  - 显示所有CPU核心的使用情况
# H  - 显示线程
# c  - 显示完整命令行
# k  - 杀死指定进程
# q  - 退出

# load average解读:
# 如果load average = 1.0,表示1个CPU核心刚好满负荷
# 如果是4核CPU,load average = 4.0表示刚好满负荷
# load average持续高于CPU核心数,说明系统过载
# 比较三个数值(1分钟、5分钟、15分钟):
#   1.5 1.2 0.8 → 负载在上升,系统越来越忙
#   0.8 1.2 1.5 → 负载在下降,系统正在恢复
vmstat------系统虚拟内存统计

vmstat就像系统的"体检报告",可以全面了解CPU、内存、I/O的状态。

bash 复制代码
# 每秒输出一次,共输出5次
vmstat 1 5
# 输出:
# procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
#  r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
#  2  0      0 200000 500000 1500000    0    0    10    20  500  800 25 10 63  2  0
#  3  0      0 190000 500000 1510000    0    0    50    30  600  900 30 15 53  2  0

# 关键列解读:
# r  - 运行队列中的进程数(持续大于CPU核心数说明CPU不够用)
# b  - 等待I/O的进程数(持续大于0说明I/O有瓶颈)
# si - 每秒从swap读入内存的数据量(持续大于0说明内存不足)
# so - 每秒从内存写入swap的数据量(同上)
# bi - 每秒从块设备读取的块数(磁盘读)
# bo - 每秒向块设备写入的块数(磁盘写)
# us - 用户态CPU时间
# sy - 内核态CPU时间
# id - 空闲CPU时间
# wa - I/O等待时间(持续大于20%说明磁盘I/O是瓶颈)
找出CPU占用最高的进程
bash 复制代码
# 方法一:ps命令
ps aux --sort=-%cpu | head -10
# 按CPU使用率从高到低排序,显示前10个

# 方法二:top一次性输出
top -b -n 1 -o %CPU | head -20

# 方法三:查看指定进程的CPU使用情况
pidstat 1 5
# 每秒输出一次,共5次,显示所有进程的CPU使用率

# 方法四:查看线程级别的CPU使用
top -H -p 1234
# 查看PID为1234的进程内各线程的CPU使用情况
# 这在排查多线程程序性能问题时非常有用

18.5.3 内存性能分析

bash 复制代码
# free命令------查看内存使用概况
free -h
#               total        used        free      shared  buff/cache   available
# Mem:           7.8Gi       3.9Gi       1.2Gi       200Mi       2.7Gi       3.4Gi
# Swap:          2.0Gi          0B       2.0Gi

# 重要理解:
# free那一列不等于"可用内存"!
# available才是真正的可用内存
# buff/cache是系统用作缓存的内存,需要时会自动释放
# 所以判断内存是否不足,看available列

# /proc/meminfo------查看详细的内存信息
cat /proc/meminfo
# MemTotal:        8192000 kB
# MemFree:         1200000 kB
# MemAvailable:    3400000 kB   <-- 这个最重要
# Buffers:          500000 kB
# Cached:          2000000 kB
# SwapTotal:       2000000 kB
# SwapFree:        2000000 kB

# 查看内存占用最高的进程
ps aux --sort=-%mem | head -10

# 查看指定进程的详细内存映射
pmap -x 1234 | tail -5
# 显示进程的内存映射,最后一行是总计

18.5.4 磁盘I/O性能分析

磁盘I/O往往是系统最大的瓶颈,因为磁盘比CPU和内存慢几个数量级。

bash 复制代码
# iostat------查看磁盘I/O统计
iostat -x 1 5
# 每秒输出一次,共5次,显示扩展信息
# Device     r/s     w/s     rkB/s    wkB/s   rrqm/s  wrqm/s  %util  await
# sda       50.00   20.00   800.00   200.00    5.00    2.00  85.00   15.00
#
# 关键指标:
# r/s、w/s  - 每秒读/写次数
# %util     - 磁盘使用率(接近100%说明磁盘满负荷)
# await     - I/O请求平均等待时间(毫秒,超过20ms需关注)

# iotop------查看哪个进程在读写磁盘(需要root)
iotop -o
# -o只显示有I/O操作的进程
# 类似top,但按I/O排序

# 如果没有iotop,可以用以下方法替代:
# 方法一:通过/proc查看
for pid in $(ls /proc | grep -E '^[0-9]+$'); do
  io=$(cat /proc/$pid/io 2>/dev/null | grep read_bytes | awk '{print $2}')
  if [ "$io" -gt 0 ] 2>/dev/null; then
    name=$(cat /proc/$pid/comm 2>/dev/null)
    echo "$pid $name $io"
  fi
done | sort -k3 -n -r | head -10

# 方法二:使用pidstat
pidstat -d 1 5
# 每秒输出一次进程的I/O统计

18.5.5 综合性能分析------使用sar

sar(System Activity Reporter)是系统活动的"历史记录仪",它可以记录和回放系统的历史性能数据。

bash 复制代码
# 安装sar(sysstat包)
yum install sysstat      # CentOS
apt install sysstat      # Ubuntu

# 查看今天的CPU使用历史
sar -u
# 输出每小时一条记录的CPU使用情况

# 查看指定日期的CPU使用历史
sar -u -f /var/log/sa/sa15
# 查看每月15号的CPU数据

# 查看内存使用历史
sar -r

# 查看磁盘I/O历史
sar -d

# 查看网络流量历史
sar -n DEV

# 实时监控(每秒一次)
sar -u 1 5

18.5.6 实际案例:网站响应缓慢

故障背景:用户反映网站访问很慢,页面加载需要10秒以上。

排查过程

bash 复制代码
# 第一步:查看系统整体负载
uptime
# load average: 8.50, 8.20, 8.00   <-- 负载很高!

# 第二步:查看CPU使用情况
top -b -n 1 | head -20
# 发现mysqld的CPU使用率高达200%

# 第三步:查看内存
free -h
# available只有200MB,内存不足

# 第四步:查看磁盘I/O
iostat -x 1 3
# %util达到95%,await达到50ms,磁盘I/O是瓶颈

# 第五步:查看swap使用情况
vmstat 1 3
# si和so都不为0,说明在频繁使用swap
# 这就是系统变慢的根本原因:内存不足→使用swap→磁盘I/O飙高→系统变慢

# 解决方案:
# 1. 查看哪个进程占用内存最多
ps aux --sort=-%mem | head -5
# 发现MySQL占用了5GB内存

# 2. 检查MySQL配置
# 可能是innodb_buffer_pool_size设置过大
# 调整MySQL配置,降低内存使用

# 3. 临时增加swap空间
dd if=/dev/zero of=/swapfile bs=1M count=4096
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile

# 4. 长期方案:增加物理内存

18.5.7 小结

性能问题诊断的核心要点:

  1. 四大瓶颈:CPU、内存、磁盘I/O、网络
  2. top看整体,vmstat看细节,iostat看磁盘
  3. sar看历史数据
  4. load average持续高于CPU核心数说明过载
  5. 频繁使用swap说明内存不足
  6. 磁盘%util接近100%说明I/O瓶颈
  7. 性能问题往往相互关联(内存不足→swap→磁盘I/O飙高→系统变慢)

18.6 内存泄漏排查

18.6.1 什么是内存泄漏?------用"漏水的水桶"来类比

想象你有一个水桶(内存),你往里面倒水(分配内存),用完水后应该把水倒掉(释放内存)。但如果你用完水后忘了倒掉,水桶里的水就会越来越多,最终溢出来------这就是内存泄漏。

在程序中,内存泄漏指的是程序分配了内存但使用完后没有释放,导致可用内存越来越少,最终系统不得不使用swap甚至触发OOM Killer(内存杀手)杀掉进程。

内存泄漏的典型表现

  1. 进程的内存使用量持续增长,不下降
  2. 系统可用内存越来越少
  3. 系统开始频繁使用swap
  4. 最终进程被OOM Killer杀掉,或者系统变慢

18.6.2 检测内存泄漏

bash 复制代码
# 方法一:定期监控进程内存使用
# 使用ps记录某进程的内存使用
while true; do
  ps -p 1234 -o pid,rss,vsz,cmd --no-headers
  sleep 60
done >> /tmp/mem_monitor.log
# rss是实际使用的物理内存(KB)
# vsz是虚拟内存大小(KB)
# 如果rss持续增长不下降,很可能是内存泄漏

# 方法二:使用pidstat监控
pidstat -r -p 1234 60
# 每60秒输出一次PID 1234的内存使用情况
# 关注RSS列是否持续增长

# 方法三:查看/proc信息
cat /proc/1234/status | grep -i vm
# VmRSS:     500000 kB   <-- 实际物理内存
# VmSize:   2000000 kB   <-- 虚拟内存
# VmPeak:   2500000 kB   <-- 峰值虚拟内存
# VmHWM:     600000 kB   <-- 峰值物理内存

# 方法四:使用smem工具查看更准确的内存使用
smem -p -k | grep process_name
# smem使用PSS(Proportional Set Size)来计算内存
# 比RSS更准确地反映进程实际使用的内存

18.6.3 系统级内存监控脚本

bash 复制代码
#!/bin/bash
# 内存监控脚本,保存为 /tmp/mem_monitor.sh
# 当可用内存低于阈值时报警

THRESHOLD=500000  # 500MB,单位KB
LOGFILE=/var/log/memory_monitor.log

while true; do
    # 获取可用内存(KB)
    MEM_AVAILABLE=$(grep MemAvailable /proc/meminfo | awk '{print $2}')
    TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
    
    if [ "$MEM_AVAILABLE" -lt "$THRESHOLD" ]; then
        echo "[$TIMESTAMP] WARNING: Low memory! Available: ${MEM_AVAILABLE}KB" >> $LOGFILE
        echo "[$TIMESTAMP] Top 5 memory consumers:" >> $LOGFILE
        ps aux --sort=-%mem | head -6 >> $LOGFILE
        echo "----------------------------------------" >> $LOGFILE
    fi
    
    sleep 60
done

18.6.4 OOM Killer详解

当系统内存严重不足时,Linux的OOM Killer(Out Of Memory Killer)会自动杀掉一些进程来释放内存。这就像救生艇上人太多了要扔掉一些行李来减轻重量。

bash 复制代码
# 查看OOM Killer的日志
dmesg | grep -i "out of memory"
dmesg | grep -i "oom-killer"
journalctl -k | grep -i oom

# OOM Killer日志示例:
# [12345.678] Out of memory: Kill process 5678 (java) score 850 or sacrifice child
# [12345.678] Killed process 5678 (java) total-vm:5000000kB, anon-rss:4000000kB

# OOM Killer选择杀谁的标准是"oom_score"
# 查看进程的oom_score
cat /proc/1234/oom_score
# 数值越高,越可能被杀

# 查看oom_score_adj(可以调整)
cat /proc/1234/oom_score_adj
# 范围-1000到1000
# -1000表示禁止被OOM Killer杀掉
# 1000表示优先被杀掉

# 保护重要进程不被OOM Killer杀掉
echo -1000 > /proc/1234/oom_score_adj

# 或者在/etc/sysctl.conf中配置
# vm.overcommit_memory = 1   # 允许过度分配
# vm.panic_on_oom = 1        # OOM时内核panic而不是杀进程(不推荐生产环境用)

18.6.5 使用valgrind排查C/C++程序的内存泄漏

如果是C/C++程序,valgrind是排查内存泄漏的神器。它就像一个"内存审计员",记录每一块内存的分配和释放。

bash 复制代码
# 安装valgrind
yum install valgrind    # CentOS
apt install valgrind    # Ubuntu

# 使用valgrind检查程序的内存泄漏
valgrind --leak-check=full --show-leak-kinds=all ./your_program

# 输出示例:
# ==12345== HEAP SUMMARY:
# ==12345==     in use at exit: 500 bytes in 5 blocks
# ==12345==   total heap usage: 100 allocs, 95 frees, 10,000 bytes allocated
# ==12345== 
# ==12345== 500 bytes in 5 blocks are definitely lost in loss record 1 of 1
# ==12345==    at 0x4C29F73: malloc (vg_replace_malloc.c:309)
# ==12345==    by 0x1086A8: create_buffer (main.c:42)
# ==12345==    by 0x1086C4: main (main.c:15)
# ==12345== 
# ==12345== LEAK SUMMARY:
# ==12345==    definitely lost: 500 bytes in 5 blocks
# 上面的输出告诉我们:在main.c第42行的create_buffer函数中分配了内存但没释放

# 注意:valgrind会使程序运行变慢20-30倍,不要在生产环境使用

18.6.6 Java程序内存泄漏排查

Java程序是内存泄漏的重灾区。JVM自带的工具可以帮助排查:

bash 复制代码
# 查看Java进程
jps -l
# 12345 com.example.MyApp

# 查看Java堆内存使用情况
jmap -heap 12345
# 输出堆的配置和使用详情

# 查看内存中各类对象的数量和大小
jmap -histo 12345 | head -20
# num     #instances         #bytes  class name
#    1:       1000000      100000000  java.lang.String
#    2:        500000       40000000  java.util.HashMap$Node

# 导出堆dump文件用于离线分析
jmap -dump:format=b,file=/tmp/heapdump.hprof 12345
# 然后用MAT (Memory Analyzer Tool) 或 VisualVM 分析

# 使用jstat监控GC情况
jstat -gcutil 12345 1000 10
# 每秒输出一次,共10次
#  S0     S1     E      O      M     CCS    YGC    YGCT    FGC   FGCT    GCT
#  0.00  45.00  60.00  85.00  90.00  85.00    20   0.500     3   1.200   1.700
# 如果O(Old区)持续增长不下降,且FGC(Full GC)频繁,说明内存泄漏

18.6.7 实际案例:Python程序内存泄漏

故障背景:一个Python数据处理脚本运行一段时间后被OOM Killer杀掉。

排查过程

bash 复制代码
# 1. 确认是内存泄漏
ps aux | grep python
# 发现RSS持续增长

# 2. 使用tracemalloc(Python内置)追踪内存分配
# 在Python代码中加入:
# import tracemalloc
# tracemalloc.start()
# # ... 你的代码 ...
# snapshot = tracemalloc.take_snapshot()
# top_stats = snapshot.statistics('lineno')
# for stat in top_stats[:10]:
#     print(stat)

# 3. 使用memory_profiler
pip install memory_profiler
# 在代码中加入:
# from memory_profiler import profile
# @profile
# def my_func():
#     ...

# 4. 使用objgraph查看对象引用
pip install objgraph
# 在代码中:
# import objgraph
# objgraph.show_most_common_types()

# 常见Python内存泄漏原因:
# 1. 全局列表/字典不断追加但不清理
# 2. 闭包引用了不需要的变量
# 3. 缓存没有大小限制
# 4. 数据库连接/文件句柄未关闭
# 5. 循环引用(虽然有GC,但有些情况会漏掉)

18.6.8 小结

内存泄漏排查的核心要点:

  1. 内存泄漏就像水桶漏水,内存只增不减
  2. 用ps/pidstat/proc监控内存增长趋势
  3. OOM Killer会在内存耗尽时杀进程,可以调整oom_score保护重要进程
  4. C/C++用valgrind排查,Java用jmap/jstat,Python用tracemalloc/memory_profiler
  5. 生产环境要注意监控,在OOM之前发现问题

18.7 磁盘空间不足处理

18.7.1 磁盘空间不足------用"衣柜塞满了"来类比

磁盘空间不足就像你的衣柜塞满了衣服,再也没法放新衣服了。这时候你有两个选择:一是清理掉不需要的旧衣服(删除文件),二是换个更大的衣柜(扩容磁盘)。

但有时候你会发现一个诡异的现象:明明删了很多文件,但磁盘空间还是满的!这就像你把旧衣服扔了,但它们还堆在衣柜里没被收走。我们后面会解释原因。

18.7.2 查看磁盘空间使用情况

bash 复制代码
# df命令------查看文件系统整体使用情况
df -h
# Filesystem      Size  Used Avail Use% Mounted on
# /dev/sda1        50G   45G    2G  96% /         <-- 96%!快满了!
# /dev/sda2       100G   60G   35G  60% /home
# tmpfs           2.0G     0  2.0G   0% /tmp

# -h: 人类可读格式(GB、MB)
# -i: 查看inode使用情况(有时候inode满了但空间没满)
df -i
# Filesystem     Inodes IUsed IFree IUse% Mounted on
# /dev/sda1      500000 490000 10000  98% /    <-- inode满了!

# du命令------查看目录大小
du -sh /var/log
# 5.0G  /var/log

# 查看当前目录下各子目录的大小,按大小排序
du -sh /* 2>/dev/null | sort -rh | head -10
# 30G  /var
# 20G  /home
# 10G  /usr
# 5G   /tmp

# 查找大于1G的文件
find / -type f -size +1G 2>/dev/null
# /var/log/mysql/slow.log   2.5G
# /home/user/backup.tar.gz  1.8G
# /tmp/big_temp_file        1.2G

# 查找最近7天修改的大文件
find / -type f -size +500M -mtime -7 2>/dev/null

18.7.3 清理磁盘空间------按"从安全到激进"的顺序

第一步:清理日志文件(最安全)
bash 复制代码
# 查看日志目录大小
du -sh /var/log
# 如果日志很大,可以清理

# 方法一:使用journalctl清理systemd日志
journalctl --disk-usage
# 查看日志占用空间

# 只保留最近7天的日志
journalctl --vacuum-time=7d

# 只保留500MB的日志
journalctl --vacuum-size=500M

# 方法二:清空日志文件内容(保留文件但清空内容)
# 注意:不要直接删除日志文件!有些服务会一直写同一个文件
# 正确做法是清空内容:
> /var/log/messages       # 清空文件内容
cat /dev/null > /var/log/messages  # 另一种写法

# 方法三:使用logrotate自动管理日志
# logrotate会自动压缩、轮转、删除旧日志
cat /etc/logrotate.conf
cat /etc/logrotate.d/syslog

# 手动执行logrotate
logrotate -f /etc/logrotate.d/syslog
第二步:清理软件包缓存
bash 复制代码
# CentOS/RHEL
yum clean all          # 清理所有缓存
package-cleanup --oldkernels --count=2  # 只保留2个旧内核

# Ubuntu/Debian
apt clean              # 清理下载的deb包
apt autoremove         # 删除不需要的依赖包

# 查看旧内核占用的空间
rpm -qa kernel | sort -V
# kernel-3.10.0-1160.el7.x86_64
# kernel-3.10.0-1160.45.1.el7.x86_64
# kernel-3.10.0-1160.66.1.el7.x86_64

# 删除旧内核(保留最新的)
yum remove kernel-3.10.0-1160.el7
第三步:清理临时文件
bash 复制代码
# 清理/tmp目录
# 注意:不要直接删除/tmp下的文件,有些程序正在使用
# 安全做法:只删除7天前的临时文件
find /tmp -type f -atime +7 -delete

# 清理用户缓存
rm -rf ~/.cache/*

# 清理系统缓存
# 注意:buff/cache不需要手动清理,系统会自动管理
# 但如果确实需要强制清理:
sync && echo 3 > /proc/sys/vm/drop_caches
# 1: 清空pagecache
# 2: 清空dentries和inodes
# 3: 清空所有缓存(包括1和2)
第四步:查找并删除大文件
bash 复制代码
# 查找前20个最大的文件
find / -type f -exec du -h {} + 2>/dev/null | sort -rh | head -20

# 查找特定目录下的大文件
find /var -type f -size +100M -exec ls -lh {} \;

# 查找大文件并按大小排序
du -ah / | sort -rh | head -20

18.7.4 "删了文件但空间没释放"的谜题

这是一个极其经典的故障场景,几乎每个运维人员都遇到过。

现象 :你用 rm 删了一个大文件,df 显示磁盘还是满的,但 ls 已经看不到那个文件了。

原因:有进程正在使用这个文件。在Linux中,如果一个文件被进程打开,即使你删除了文件名,文件的实际数据块不会被释放,直到所有打开该文件的进程都关闭它。这就像你把一份文件从文件柜中取出来(删除文件名),但有人还在看这份文件(进程还在使用),你没法把它扔掉(释放空间)。

排查与解决

bash 复制代码
# 第一步:找出哪些被删除的文件还被进程占用
lsof | grep deleted
# COMMAND   PID  USER   FD   TYPE  SIZE/OFF  NODE   NAME
# mysqld    1234 mysql  5w   REG   8,1       5000000000 12345 /var/log/mysql/slow.log (deleted)
# nginx     5678 nginx  3w   REG   8,1       2000000000 67890 /var/log/nginx/access.log (deleted)

# 上面的输出说明:
# mysqld进程(PID 1234)还在写一个已删除的5GB日志文件
# nginx进程(PID 5678)还在写一个已删除的2GB日志文件

# 第二步:解决方法
# 方法一:重启相关服务(最简单)
systemctl restart mysql
systemctl restart nginx

# 方法二:不重启服务,通过/proc恢复
# 找到进程的文件描述符
ls -la /proc/1234/fd/ | grep deleted
# lrwx------ 1 mysql mysql 64 ... 5w -> /var/log/mysql/slow.log (deleted)

# 清空文件内容(不需要关闭文件描述符)
cat /dev/null > /proc/1234/fd/5
# 这样文件内容被清空,空间被释放,但进程还能继续写

# 方法三: truncate命令
truncate -s 0 /proc/1234/fd/5

18.7.5 inode耗尽问题

有时候 df -h 显示磁盘空间还很多,但就是无法创建新文件,报错 No space left on device。这很可能是inode耗尽了。

inode就像衣柜里的衣架------衣服(数据)不多,但衣架(inode)用完了,也没法挂新衣服了。

bash 复制代码
# 查看inode使用情况
df -i
# Filesystem     Inodes  IUsed  IFree IUse% Mounted on
# /dev/sda1      500000 499990     10  100% /    <-- inode满了!

# 找出哪个目录下的文件数量最多
for d in /*; do
  echo $(find $d -type f 2>/dev/null | wc -l) $d
done | sort -rn | head -10

# 通常/var/spool或/tmp下会有大量小文件
# 常见原因:
# 1. 邮件队列堆积(/var/spool/postfix/maildrop/)
# 2. session文件堆积(/tmp/或/var/lib/php/session/)
# 3. 定时任务产生的日志碎片

# 清理大量小文件
# 警告:如果文件数量非常多(百万级),rm可能很慢甚至失败
# 使用rsync删除大量文件更快:
mkdir /tmp/empty_dir
rsync --delete /tmp/empty_dir/ /path/to/dir/with/many/files/

# 或者使用find批量删除
find /path/to/dir -type f -delete

18.7.6 磁盘扩容

当清理空间不够用时,就需要扩容磁盘了。

bash 复制代码
# ===== LVM扩容(推荐方式) =====
# LVM(Logical Volume Manager)允许动态调整分区大小
# 就像衣柜可以随时加隔板扩大空间

# 第一步:查看当前LVM状态
lvdisplay
# LV Path: /dev/centos/root
# LV Size: 50.00 GiB

# 第二步:查看是否有空闲空间
vgdisplay
# Free  PE / Size: 0 / 0    <-- 没有空闲空间,需要先扩展物理卷

# 第三步:添加新硬盘后,创建物理卷
pvcreate /dev/sdb

# 第四步:扩展卷组
vgextend centos /dev/sdb

# 第五步:扩展逻辑卷(使用所有空闲空间)
lvextend -l +100%FREE /dev/centos/root

# 或者指定大小
lvextend -L +20G /dev/centos/root

# 第六步:扩展文件系统
# ext4文件系统:
resize2fs /dev/centos/root

# xfs文件系统:
xfs_growfs /

# 验证
df -h

18.7.7 磁盘空间监控脚本

bash 复制代码
#!/bin/bash
# 磁盘空间监控脚本
# 保存为 /usr/local/bin/disk_monitor.sh

THRESHOLD=80  # 使用率阈值(%)
EMAIL="admin@example.com"

df -h | awk 'NR>1' | while read line; do
    # 提取使用率(去掉%号)
    usage=$(echo $line | awk '{print $5}' | sed 's/%//')
    partition=$(echo $line | awk '{print $6}')
    
    if [ "$usage" -gt "$THRESHOLD" ]; then
        echo "警告:分区 $partition 使用率已达 ${usage}%!"
        echo "详细信息:$line"
        # 发送邮件通知
        # echo "磁盘空间告警" | mail -s "磁盘告警" $EMAIL
    fi
done

18.7.8 小结

磁盘空间不足处理的核心要点:

  1. df查看整体空间,du查看目录大小,find查找大文件
  2. 清理顺序:日志→缓存→临时文件→大文件
  3. "删了文件但空间没释放"------用lsof找被占用的已删除文件
  4. inode耗尽------df -i查看,清理大量小文件
  5. 空间不够时用LVM扩容
  6. 设置监控脚本,提前预警

18.8 进程僵死与处理

18.8.1 进程状态详解------用"办公室工作人员"来类比

Linux中的进程有多种状态,就像办公室里的工作人员有不同的工作状态:

状态 代码 类比
运行 R (Running) 正在工作
可中断睡眠 S (Sleeping) 在等快递,快递来了就继续工作
不可中断睡眠 D (Disk wait) 在等电梯,不能打断
停止 T (Stopped) 暂停工作(比如按了Ctrl+Z)
僵尸 Z (Zombie) 人已经走了但档案还在
bash 复制代码
# 查看进程状态
ps aux
# STAT列就是进程状态
#   R  - 运行
#   S  - 可中断睡眠
#   D  - 不可中断睡眠
#   T  - 停止
#   Z  - 僵尸
#   <  - 高优先级
#   N  - 低优先级
#   s  - 会话首进程
#   l  - 多线程
#   +  - 前台进程组

# 查看所有进程的详细状态
ps -eo pid,ppid,stat,cmd | more

18.8.2 僵尸进程------"走了但没被销户"

僵尸进程(Zombie Process)是最让人困惑的进程状态。要理解它,需要先理解父子进程的关系。

在Linux中,每个进程都有一个父进程。当子进程完成任务退出时,它会变成"僵尸"------进程已经死了,但它的"退出信息"还保留着,等待父进程来读取。如果父进程没有来读取这个退出信息,子进程就会一直保持僵尸状态。

这就像员工离职了,但HR没有办理离职手续(销户),这个员工的档案就一直挂在公司里。虽然员工不会再消耗工资(CPU、内存),但档案占着名额(进程号PID)。

bash 复制代码
# 查找僵尸进程
ps aux | grep 'Z'
# 或者更精确地查找
ps -eo pid,ppid,stat,cmd | awk '$3 ~ /Z/'

# 输出示例:
# PID    PPID  STAT  CMD
# 12345  12340 Z     [myapp] <defunct>

# 上面的输出说明:
# PID 12345 是僵尸进程
# 它的父进程是 PID 12340

# 统计僵尸进程数量
ps aux | awk '{print $8}' | grep -c Z

18.8.3 清理僵尸进程

僵尸进程本身不能被kill直接杀掉(因为它已经死了!),你需要"杀"的是它的父进程,或者让父进程去回收它。

bash 复制代码
# 第一步:找到僵尸进程的父进程
ps -eo pid,ppid,stat,cmd | awk '$3 ~ /Z/'
# 得到父进程PID(PPID列)

# 第二步:尝试让父进程回收子进程
# 发送SIGCHLD信号给父进程,提醒它去回收子进程
kill -CHLD <父进程PID>

# 第三步:如果父进程没有响应,只能杀掉父进程
kill <父进程PID>

# 如果普通kill无效,使用SIGKILL强制杀掉
kill -9 <父进程PID>

# 注意:杀掉父进程后,僵尸进程会被init(PID 1)接管
# init会自动回收僵尸进程

# 第四步:如果父进程是init(PID 1)且僵尸进程还在
# 这通常意味着系统有问题,可能需要重启
# 但在重启前,检查是不是有内核问题
dmesg | tail -20

18.8.4 不可中断睡眠进程(D状态)

比僵尸进程更棘手的是D状态(Uninterruptible Sleep)的进程。这种进程正在等待I/O操作完成,不能被信号中断------连 kill -9 都杀不掉!

bash 复制代码
# 查找D状态的进程
ps aux | awk '$8 ~ /D/'

# D状态进程通常在等待磁盘I/O
# 常见原因:
# 1. 磁盘故障导致I/O操作卡住
# 2. NFS挂载的服务器无响应
# 3. 存储网络断开

# 解决方法:
# 1. 修复底层的I/O问题(如恢复NFS服务器)
# 2. 如果是NFS问题,可以强制卸载
umount -f /mnt/nfs
umount -l /mnt/nfs    # lazy unmount

# 3. 如果以上都不行,可能只能重启系统
# 但D状态进程可能导致重启也卡住
# 这时只能从硬件层面强制重启

# 预防方法:
# 1. 使用NFS时加soft选项,避免无限等待
# mount -o soft,timeo=30 server:/share /mnt/nfs
# 2. 定期检查磁盘健康
# 3. 配置存储多路径

18.8.5 孤儿进程

孤儿进程(Orphan Process)和僵尸进程相反------父进程先退出了,子进程还在运行。这些子进程会被init(PID 1)"收养",成为孤儿进程。

孤儿进程本身不是问题,init会负责管理它们。但如果一个程序产生了大量孤儿进程且不清理,就会占用系统资源。

bash 复制代码
# 查找孤儿进程(父进程是init/PID 1的进程)
ps -eo pid,ppid,cmd | awk '$2 == 1'

# 孤儿进程通常不需要特别处理
# 但如果数量过多,说明程序设计有问题

18.8.6 进程管理常用命令

bash 复制代码
# kill命令------发送信号给进程
kill -l              # 列出所有信号
kill 1234            # 发送SIGTERM(优雅退出),默认信号
kill -9 1234         # 发送SIGKILL(强制杀掉),不可被忽略
kill -15 1234        # 发送SIGTERM,和kill 1234一样
kill -HUP 1234       # 发送SIGHUP,通常用于让进程重新加载配置
kill -USR1 1234      # 发送SIGUSR1,自定义信号
kill -STOP 1234      # 暂停进程(不是终止)
kill -CONT 1234      # 恢复暂停的进程

# killall命令------按进程名杀进程
killall nginx        # 杀掉所有名为nginx的进程
killall -9 nginx     # 强制杀掉
killall -u mysql     # 杀掉mysql用户的所有进程

# pkill命令------更灵活的按名杀进程
pkill nginx          # 和killall nginx类似
pkill -u mysql       # 杀掉mysql用户的进程
pkill -f "python script.py"  # 按完整命令行匹配

# 常用信号说明:
# 1  SIGHUP   - 挂起信号,常用于重新加载配置
# 2  SIGINT   - 中断信号,相当于Ctrl+C
# 3  SIGQUIT  - 退出信号,相当于Ctrl+\
# 9  SIGKILL  - 强制杀掉,不可被捕获或忽略
# 15 SIGTERM  - 优雅终止,默认信号
# 18 SIGCONT  - 恢复运行
# 19 SIGSTOP  - 暂停运行

18.8.7 实际案例:进程卡死导致服务不可用

故障背景:Nginx服务无法响应请求,重启命令也卡住不动。

排查过程

bash 复制代码
# 1. 查看nginx进程状态
ps aux | grep nginx
# 发现nginx worker进程都是D状态(不可中断睡眠)

# 2. 查看进程在等待什么
cat /proc/<nginx_pid>/stack
# 或者
cat /proc/<nginx_pid>/wchan
# 发现都在等待磁盘I/O

# 3. 检查磁盘状态
iostat -x 1 3
# %util 100%,磁盘完全卡住了

# 4. 检查dmesg
dmesg | tail -20
# 发现大量磁盘错误:
# sd 0:0:0:0: [sda] tag#0 FAILED Result: hostbyte=DID_ERROR
# 这说明磁盘硬件故障

# 5. 解决方案
# 短期:强制杀掉nginx进程
kill -9 <nginx_pid>
# 如果kill -9也无效(因为进程在D状态),只能等待I/O超时

# 长期:更换故障磁盘,恢复数据

18.8.8 小结

进程僵死与处理的核心要点:

  1. 理解进程状态:R(运行)、S(睡眠)、D(不可中断睡眠)、T(停止)、Z(僵尸)
  2. 僵尸进程是子进程已死但父进程没有回收,需要杀父进程来清理
  3. D状态进程无法被kill,需要修复底层I/O问题
  4. 常用信号:SIGTERM(15)优雅退出,SIGKILL(9)强制杀掉
  5. kill/pkill/killall是进程管理三剑客

18.9 系统日志分析技巧

18.9.1 日志的重要性------用"侦探破案"来类比

系统日志就是Linux留给你的"案发现场"。当故障发生后,系统会在日志中留下大量线索。优秀的运维工程师就像优秀的侦探,能从海量日志中找到关键证据。

Linux系统的日志就像城市的监控摄像头网络:

  • /var/log/messages(或/var/log/syslog)------城市总监控
  • /var/log/secure(或/var/log/auth.log)------门禁系统记录
  • /var/log/dmesg------系统启动时的监控
  • /var/log/nginx/access.log------某个建筑的进出记录
  • journalctl------可以查询所有监控的智能系统

18.9.2 日志系统架构

现代Linux使用两种日志系统:

传统syslog

bash 复制代码
# 日志配置文件
cat /etc/rsyslog.conf
cat /etc/rsyslog.d/*.conf

# 日志级别(从高到低):
# emerg   - 紧急:系统不可用
# alert   - 警报:必须立即处理
# crit    - 严重:严重情况
# err     - 错误:错误信息
# warn    - 警告:警告信息
# notice  - 通知:正常但重要
# info    - 信息:一般信息
# debug   - 调试:调试信息

# 日志位置说明:
# /var/log/messages  - 系统主日志(CentOS)
# /var/log/syslog    - 系统主日志(Ubuntu)
# /var/log/secure    - 安全日志(CentOS)
# /var/log/auth.log  - 认证日志(Ubuntu)
# /var/log/maillog   - 邮件日志
# /var/log/cron      - 定时任务日志
# /var/log/boot.log  - 启动日志
# /var/log/dmesg     - 内核环缓冲日志

systemd journal

bash 复制代码
# journalctl是systemd的日志查询工具,功能非常强大

# 查看所有日志
journalctl

# 查看最近的日志(类似tail -f)
journalctl -f

# 查看今天的日志
journalctl --since today

# 查看指定时间范围的日志
journalctl --since "2024-01-15 10:00:00" --until "2024-01-15 12:00:00"
journalctl --since "1 hour ago"
journalctl --since "30 min ago"

# 查看指定服务的日志
journalctl -u nginx.service
journalctl -u nginx.service --since today

# 查看指定进程的日志
journalctl _PID=1234

# 按日志级别过滤
journalctl -p err        # 只看error及以上
journalctl -p err -p warning  # 错误和警告

# 查看本次启动的日志
journalctl -b

# 查看上一次启动的日志
journalctl -b -1

# 查看内核日志
journalctl -k

# 以JSON格式输出(方便程序处理)
journalctl -o json

# 显示日志尾部
journalctl -n 50    # 最后50条

18.9.3 日志分析实战技巧

技巧一:grep过滤关键信息
bash 复制代码
# 在系统日志中查找错误
grep -i error /var/log/messages
grep -i "fail" /var/log/messages
grep -i "panic" /var/log/messages

# 查找指定时间段的错误
awk '/Jan 15 10:/' /var/log/messages | grep -i error

# 统计各种错误出现的次数
grep -i error /var/log/messages | awk '{print $5,$6,$7}' | sort | uniq -c | sort -rn | head -20

# 查找包含多个关键词的行
grep -E "error|fail|critical" /var/log/messages

# 排除某些关键词
grep -v "INFO" /var/log/app.log | grep -i error

# 显示匹配行及上下文
grep -C 5 "error" /var/log/messages   # 显示匹配行前后各5行
grep -B 5 "error" /var/log/messages   # 显示匹配行前5行
grep -A 5 "error" /var/log/messages   # 显示匹配行后5行
技巧二:awk提取和统计
bash 复制代码
# 提取日志中的IP地址并统计访问次数
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

# 统计HTTP状态码分布
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
#    50000 200    <-- 200状态码50000次
#     1000 404    <-- 404状态码1000次
#      500 500    <-- 500状态码500次

# 统计每小时的访问量
awk '{print $4}' /var/log/nginx/access.log | cut -c 14-15 | sort | uniq -c
# $4是时间字段,截取第14-15位(小时部分)

# 找出访问量最大的URL
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

# 计算请求的平均响应时间(假设日志中有响应时间字段)
awk '{sum += $NF; count++} END {print "平均响应时间:", sum/count, "ms"}' /var/log/nginx/access.log
技巧三:实时监控日志
bash 复制代码
# tail -f 实时监控日志
tail -f /var/log/messages

# 同时监控多个日志文件
tail -f /var/log/messages /var/log/secure

# 只显示包含error的行
tail -f /var/log/messages | grep --line-buffered -i error

# 使用journalctl实时监控
journalctl -f
journalctl -f -u nginx

# 多功能日志监控工具 multitail
# 安装:yum install multitail 或 apt install multitail
multitail /var/log/messages /var/log/secure
技巧四:日志轮转管理
bash 复制代码
# logrotate配置
cat /etc/logrotate.conf
# 全局配置

cat /etc/logrotate.d/nginx
# /var/log/nginx/*.log {
#     daily           # 每天轮转
#     rotate 30       # 保留30个旧日志
#     compress        # 压缩旧日志
#     delaycompress   # 延迟压缩(最近的旧日志不压缩)
#     missingok       # 日志不存在不报错
#     notifempty      # 日志为空不轮转
#     create 644 nginx nginx  # 创建新日志的权限和属主
#     postrotate      # 轮转后执行的命令
#         if [ -f /var/run/nginx.pid ]; then
#             kill -USR1 `cat /var/run/nginx.pid`
#         fi
#     endscript
# }

# 手动测试logrotate
logrotate -d /etc/logrotate.d/nginx   # 调试模式,不实际执行
logrotate -f /etc/logrotate.d/nginx   # 强制执行轮转

18.9.4 分析登录和安全日志

bash 复制代码
# 查看成功登录的记录
last
# root   pts/0  192.168.1.50  Mon Jan 15 10:30   still logged in
# user   pts/1  10.0.0.100    Mon Jan 15 09:00 - 10:00  (01:00)

# 查看失败登录的记录
lastb
# 或者查看安全日志
grep "Failed password" /var/log/secure     # CentOS
grep "Failed password" /var/log/auth.log   # Ubuntu

# 统计失败登录的IP(防暴力破解)
grep "Failed password" /var/log/secure | awk '{print $11}' | sort | uniq -c | sort -rn | head -20
#     100 192.168.1.200   <-- 这个IP尝试了100次!
#      50 10.0.0.99
#      30 172.16.0.5

# 查看SSH登录记录
journalctl -u sshd --since today

# 查看谁在使用sudo
grep "sudo" /var/log/secure

# 查看用户创建和删除记录
grep "useradd\|userdel" /var/log/secure

18.9.5 实际案例:通过日志定位Web服务502错误

故障背景:用户访问网站时频繁出现502 Bad Gateway错误。

排查过程

bash 复制代码
# 1. 查看Nginx错误日志
tail -100 /var/log/nginx/error.log
# 发现大量类似错误:
# connect() to unix:/var/run/php-fpm.sock failed (11: Resource temporarily unavailable)

# 2. 说明PHP-FPM无法处理更多请求了
# 查看PHP-FPM状态
systemctl status php-fpm

# 3. 查看PHP-FPM日志
tail -100 /var/log/php-fpm/error.log
# 发现:WARNING: [pool www] server reached pm.max_children setting (50)

# 4. 说明PHP-FPM的子进程数达到了上限
# 查看当前配置
grep "max_children" /etc/php-fpm.d/www.conf
# pm.max_children = 50

# 5. 查看PHP-FPM子进程的内存使用
ps -C php-fpm -o pid,rss,cmd --sort=-rss | head
# 发现每个子进程占用100MB+,50个就是5GB

# 6. 解决方案:优化PHP-FPM配置
# 增加max_children或减少每个进程的内存使用
# 修改配置后重启
systemctl restart php-fpm

# 7. 验证:继续观察日志
tail -f /var/log/nginx/error.log
# 502错误消失

18.9.6 集中式日志管理

当服务器数量增多时,逐台查看日志效率太低。这时需要集中式日志管理:

bash 复制代码
# 常见的集中式日志方案:
# 1. ELK Stack (Elasticsearch + Logstash + Kibana)
# 2. Graylog
# 3. Loki + Grafana

# 简单的远程日志转发(通过rsyslog)
# 在客户端配置
cat /etc/rsyslog.d/remote.conf
# *.* @@logserver.example.com:514    # TCP转发
# 或
# *.* @logserver.example.com:514     # UDP转发

# 重启rsyslog
systemctl restart rsyslog

18.9.7 小结

系统日志分析的核心要点:

  1. 日志是破案的关键证据,出了问题第一时间看日志
  2. journalctl是systemd日志的瑞士军刀,学会各种过滤参数
  3. grep+awk+sort+uniq是日志分析的黄金组合
  4. 设置合理的日志轮转策略,防止日志占满磁盘
  5. 服务器多时使用集中式日志管理(ELK等)

18.10 单用户模式与救援模式

18.10.1 什么是单用户模式?------用"急诊室"来类比

当系统出了严重问题无法正常启动时,你需要一个"急诊室"------这就是单用户模式和救援模式。

单用户模式就像医院的急诊室:

  1. 只有一个医生值班(单用户)
  2. 不对外开放(没有网络服务)
  3. 大部分设备关闭(只启动基本服务)
  4. 可以做紧急处理(修改配置、修复文件系统)

救援模式就像医疗队的野外急救------当系统自身已经无法启动时,用外部工具(Live CD/U盘)来抢救。

18.10.2 进入单用户模式

方法一:通过GRUB菜单进入(CentOS 7 / RHEL 7+)
bash 复制代码
# 1. 重启系统,在GRUB菜单界面按 e 键编辑启动项
# 2. 找到以 linux16 或 linux 开头的行
# 3. 在该行末尾添加:
#    rd.break
#    或者
#    systemd.unit=rescue.target
#    或者(更早期的单用户模式)
#    1  或  single

# 4. 按 Ctrl+X 启动

# 使用 rd.break 进入的步骤:
# 系统会停在一个紧急维修shell
# 此时根文件系统以只读方式挂载在 /sysroot

# 重新挂载为读写
switch_root:/# mount -o remount,rw /sysroot

# chroot到根文件系统
switch_root:/# chroot /sysroot

# 现在你可以修改任何文件了
# 例如:重置root密码
sh-4.2# passwd root
# 输入新密码

# 如果SELinux是enforcing模式,需要创建标记文件
sh-4.2# touch /.autorelabel

# 退出并重启
sh-4.2# exit
switch_root:/# exit
# 系统会自动重启
方法二:修改GRUB进入单用户模式(Ubuntu/Debian)
bash 复制代码
# 1. 重启系统,在GRUB菜单界面按 e 键
# 2. 找到以 linux 开头的行
# 3. 将 ro(只读)改为 rw(读写)
# 4. 在行末添加:
#    init=/bin/bash
#    或者
#    systemd.unit=rescue.target

# 5. 按 Ctrl+X 或 F10 启动

# 使用 init=/bin/bash 的步骤:
# 系统直接进入bash shell,不需要密码

# 重新挂载根文件系统为读写
root@(none):/# mount -o remount,rw /

# 修改需要的文件
# 例如重置root密码
root@(none):/# passwd root

# 重启
root@(none):/# exec /sbin/init
# 或者
root@(none):/# reboot -f

18.10.3 救援模式------使用Live CD/USB

当系统完全无法启动,连GRUB都进不去时,就需要用Live CD/USB来救援了。

bash 复制代码
# 第一步:用Live USB启动,选择"Try Ubuntu"或类似选项

# 第二步:打开终端,查看磁盘分区
sudo fdisk -l
# 或者
lsblk
# 找到原来的根分区,比如 /dev/sda1

# 第三步:挂载原系统分区
sudo mount /dev/sda1 /mnt

# 如果有独立的/boot分区,也要挂载
sudo mount /dev/sda2 /mnt/boot

# 如果有/boot/efi分区(UEFI系统)
sudo mount /dev/sda3 /mnt/boot/efi

# 第四步:挂载虚拟文件系统(chroot需要)
sudo mount --bind /dev /mnt/dev
sudo mount --bind /dev/pts /mnt/dev/pts
sudo mount --bind /proc /mnt/proc
sudo mount --bind /sys /mnt/sys
sudo mount --bind /run /mnt/run

# 第五步:chroot到原系统
sudo chroot /mnt

# 现在你就在原系统的环境中了!
# 可以执行任何修复操作:
# - 修改配置文件
# - 修复GRUB
# - 修复文件系统
# - 安装/卸载软件包

# 第六步:修复完成后退出
exit

# 第七步:卸载所有挂载
sudo umount /mnt/run
sudo umount /mnt/sys
sudo umount /mnt/proc
sudo umount /mnt/dev/pts
sudo umount /mnt/dev
sudo umount /mnt/boot/efi  # 如果挂载了
sudo umount /mnt/boot       # 如果挂载了
sudo umount /mnt

# 第八步:重启
sudo reboot

18.10.4 使用CentOS/RHEL安装盘进入救援模式

bash 复制代码
# 1. 用CentOS安装光盘/U盘启动
# 2. 在安装菜单选择 "Troubleshooting"
# 3. 选择 "Rescue a CentOS system"
# 4. 选择继续模式(Continue)
#    - Continue: 读写模式挂载,最常用
#    - Read-Only: 只读模式
#    - Skip: 不挂载,直接进入shell
#    - Advanced: 高级选项

# 5. 系统会自动挂载原系统到 /mnt/sysimages
# 6. 提示你执行 chroot /mnt/sysimages

# 执行chroot
chroot /mnt/sysimages

# 现在可以进行修复了
# 常见修复操作:

# 修复GRUB
grub2-install /dev/sda
grub2-mkconfig -o /boot/grub2/grub.cfg

# 重建initramfs
dracut --force

# 修复文件系统
fsck /dev/sda1

# 重置root密码
passwd root

# 修复fstab
vi /etc/fstab

18.10.5 实际案例:忘记root密码

这是最常见的需求------忘记了root密码无法登录系统。

bash 复制代码
# CentOS 7+ 重置root密码:

# 1. 重启,在GRUB菜单按e
# 2. 在linux16行末添加 rd.break
# 3. Ctrl+X启动
# 4. 依次执行:
mount -o remount,rw /sysroot
chroot /sysroot
passwd root
# 输入新密码两次
touch /.autorelabel    # SELinux需要
exit
exit

# Ubuntu重置root密码:

# 1. 重启,在GRUB菜单按e
# 2. 找到linux行,把ro改成rw,末尾加 init=/bin/bash
# 3. Ctrl+X或F10启动
# 4. 执行:
passwd root
# 或者
passwd <你的用户名>
# 5. 重启:
exec /sbin/init
# 或者强制重启
reboot -f

18.10.6 实际案例:GRUB损坏修复

bash 复制代码
# 场景:安装双系统后,Linux的GRUB被Windows覆盖了

# 1. 用Live USB启动
# 2. 挂载Linux分区
sudo mount /dev/sda2 /mnt
# 如果有独立的/boot分区
sudo mount /dev/sda1 /mnt/boot

# 3. 挂载虚拟文件系统
sudo mount --bind /dev /mnt/dev
sudo mount --bind /proc /mnt/proc
sudo mount --bind /sys /mnt/sys

# 4. chroot
sudo chroot /mnt

# 5. 重新安装GRUB
# BIOS系统:
grub-install /dev/sda
# UEFI系统:
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=centos

# 6. 重新生成GRUB配置
grub2-mkconfig -o /boot/grub2/grub.cfg      # CentOS
update-grub                                   # Ubuntu

# 7. 退出并重启
exit
sudo reboot

18.10.7 单用户模式的安全风险

需要注意的是,任何人只要能物理接触服务器,就能通过单用户模式重置root密码。这就像任何人只要能进入医院急诊室,就能做治疗一样。

bash 复制代码
# 保护GRUB,防止未授权进入单用户模式

# 第一步:设置GRUB密码
grub2-setpassword
# 输入密码后会生成 /boot/grub2/user.cfg

# 第二步:重新生成GRUB配置
grub2-mkconfig -o /boot/grub2/grub.cfg

# 这样以后编辑GRUB启动项就需要密码了

# Ubuntu下设置GRUB密码:
# 编辑 /etc/grub.d/40_custom
# 添加:
# set superusers="admin"
# password admin your_password
# 然后 update-grub

# 更安全的做法:使用密码哈希
grub-mkpasswd-pbkdf2
# 生成哈希后写入配置文件

18.10.8 小结

单用户模式与救援模式的核心要点:

  1. 单用户模式是系统的"急诊室",只启动基本服务
  2. 通过GRUB编辑进入单用户模式(rd.break / init=/bin/bash)
  3. 救援模式用Live CD/USB启动,通过chroot修复原系统
  4. 常见用途:重置密码、修复GRUB、修复文件系统、修改配置
  5. 注意保护GRUB,防止未授权访问

18.11 数据备份与恢复策略

18.11.1 备份的重要性------用"保险"来类比

数据备份就像买保险------没出事时你觉得浪费钱,出了事时你庆幸买了。但和保险不同的是,数据丢失不是"可能"发生,而是"迟早"会发生。硬盘会坏、人会误操作、系统会被黑、机房会断电......唯一的问题是:你的数据备份好了吗?

数据丢失的常见原因

  1. 硬件故障:硬盘坏道、服务器烧毁
  2. 人为错误:误删文件、误执行命令
  3. 软件bug:程序错误导致数据损坏
  4. 恶意攻击:勒索软件加密数据
  5. 自然灾害:火灾、水灾、地震
  6. 电力故障:突然断电导致数据损坏

18.11.2 备份的"3-2-1"黄金法则

备份领域有一个著名的"3-2-1"法则,就像买保险要买够保额一样:

  • 3 份数据副本(1份原始数据 + 2份备份)

  • 2 种不同的存储介质(如硬盘 + 磁带 / 本地 + 云)

  • 1 份异地备份(防止机房整体灾难)

    原始数据(本地) ──→ 备份1(本地其他磁盘) ──→ 备份2(异地/云端)

18.11.3 备份的分类

类型 说明 恢复速度 存储空间 类比
完全备份 备份所有数据 最快 最大 给整栋楼拍照
增量备份 只备份上次备份后变化的数据 最慢 最小 只记录新搬进来的东西
差异备份 备份上次完全备份后变化的数据 中等 中等 记录从上次拍照后所有变化
复制代码
完全备份策略示例:
周一:完全备份(100GB)
周二:差异备份(10GB)------ 周一以来变化的数据
周三:差异备份(20GB)------ 周一以来变化的数据
周四:差异备份(30GB)------ 周一以来变化的数据
周五:完全备份(105GB)------ 新的一轮开始

增量备份策略示例:
周一:完全备份(100GB)
周二:增量备份(5GB)------ 周一以来变化的数据
周三:增量备份(5GB)------ 周二以来变化的数据
周四:增量备份(5GB)------ 周三以来变化的数据
周五:增量备份(5GB)------ 周四以来变化的数据

18.11.4 常用备份工具

tar------最基础的备份工具
bash 复制代码
# 完全备份
tar -czvf /backup/full_$(date +%Y%m%d).tar.gz /home /etc /var/www

# 参数说明:
# -c: 创建归档
# -z: 使用gzip压缩
# -v: 显示过程
# -f: 指定文件名
# -p: 保留权限

# 增量备份(使用gzip+tar的增量功能)
# 第一次完全备份
tar -czvf /backup/full.tar.gz -g /backup/snapshot.snar /home
# 第二次增量备份
tar -czvf /backup/incr_$(date +%Y%m%d).tar.gz -g /backup/snapshot.snar /home
# -g参数指定快照文件,tar会根据快照判断哪些文件变化了

# 恢复
# 恢复完全备份
tar -xzvf /backup/full.tar.gz -C /
# 恢复增量备份(按顺序)
tar -xzvf /backup/full.tar.gz -C /
tar -xzvf /backup/incr_20240116.tar.gz -C /
tar -xzvf /backup/incr_20240117.tar.gz -C /

# 查看备份内容(不解压)
tar -tzvf /backup/full.tar.gz | head -20
rsync------增量同步的利器

rsync是Linux下最强大的备份工具之一,它只传输变化的数据,效率极高。

bash 复制代码
# 基本同步
rsync -avz /home/ /backup/home/
# 参数说明:
# -a: 归档模式(保留权限、时间等)
# -v: 显示详细信息
# -z: 压缩传输
# 注意:源路径末尾的/很重要!
#   /home/  表示同步/home目录下的内容
#   /home   表示把/home目录本身同步过去

# 删除目标中源端已删除的文件(完全镜像)
rsync -avz --delete /home/ /backup/home/

# 远程备份(通过SSH)
rsync -avz -e ssh /home/ user@backup-server:/backup/home/

# 使用SSH非标准端口
rsync -avz -e "ssh -p 2222" /home/ user@backup-server:/backup/home/

# 显示传输进度
rsync -avz --progress /home/ /backup/home/

# 排除不需要备份的文件
rsync -avz --exclude='*.tmp' --exclude='cache/' /home/ /backup/home/

# 使用exclude文件
echo "*.tmp" > /tmp/exclude.txt
echo "cache/" >> /tmp/exclude.txt
rsync -avz --exclude-from='/tmp/exclude.txt' /home/ /backup/home/

# 限制带宽(避免占满网络)
rsync -avz --bwlimit=10000 /home/ user@backup-server:/backup/
# --bwlimit单位是KB/s,10000表示10MB/s
dd------磁盘/分区级别的备份

dd可以备份整个磁盘或分区,就像给硬盘"拍X光片"。

bash 复制代码
# 备份整个磁盘到镜像文件
dd if=/dev/sda of=/backup/sda_image.img bs=4M status=progress
# if: 输入文件(源)
# of: 输出文件(目标)
# bs: 块大小(4M表示每次读写4MB)
# status=progress: 显示进度

# 备份MBR(主引导记录,前512字节)
dd if=/dev/sda of=/backup/mbr_backup bs=512 count=1

# 恢复MBR
dd if=/backup/mbr_backup of=/dev/sda bs=512 count=1

# 克隆磁盘(磁盘到磁盘)
dd if=/dev/sda of=/dev/sdb bs=4M status=progress

# 创建磁盘压缩镜像
dd if=/dev/sda bs=4M | gzip -c > /backup/sda_image.img.gz

# 恢复压缩镜像
gunzip -c /backup/sda_image.img.gz | dd of=/dev/sda bs=4M

# 注意:dd命令非常危险!
# if和of千万不能写反,写反了会毁掉数据!
# 使用前一定要确认好源和目标!
mysqldump------数据库备份
bash 复制代码
# 备份单个数据库
mysqldump -u root -p mydb > /backup/mydb_$(date +%Y%m%d).sql

# 备份所有数据库
mysqldump -u root -p --all-databases > /backup/all_db_$(date +%Y%m%d).sql

# 备份时压缩
mysqldump -u root -p mydb | gzip > /backup/mydb_$(date +%Y%m%d).sql.gz

# 备份指定表
mysqldump -u root -p mydb table1 table2 > /backup/tables.sql

# 仅备份表结构(不包含数据)
mysqldump -u root -p --no-data mydb > /backup/schema.sql

# 仅备份数据(不包含表结构)
mysqldump -u root -p --no-create-info mydb > /backup/data.sql

# 恢复数据库
mysql -u root -p mydb < /backup/mydb_20240115.sql

# 恢复压缩的备份
gunzip < /backup/mydb_20240115.sql.gz | mysql -u root -p mydb

18.11.5 自动化备份脚本

bash 复制代码
#!/bin/bash
# 自动备份脚本
# 保存为 /usr/local/bin/auto_backup.sh
# 加入crontab自动执行:0 2 * * * /usr/local/bin/auto_backup.sh

# ===== 配置部分 =====
BACKUP_DIR="/backup"
DATE=$(date +%Y%m%d)
DAY_OF_WEEK=$(date +%u)   # 1=周一,7=周日
LOG_FILE="/var/log/backup.log"
RETENTION_DAYS=7          # 本地备份保留天数
REMOTE_SERVER="backup@example.com"
REMOTE_DIR="/remote_backup"

# ===== 日志函数 =====
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> $LOG_FILE
}

# ===== 开始备份 =====
log "========== 开始备份 =========="

# 1. 备份配置文件
log "备份配置文件..."
tar -czf $BACKUP_DIR/etc_$DATE.tar.gz /etc 2>>$LOG_FILE
log "配置文件备份完成"

# 2. 备份数据库
log "备份数据库..."
mysqldump -u root -ppassword --all-databases | gzip > $BACKUP_DIR/mysql_$DATE.sql.gz 2>>$LOG_FILE
log "数据库备份完成"

# 3. 备份网站文件
log "备份网站文件..."
rsync -a /var/www/ $BACKUP_DIR/www_$DATE/ 2>>$LOG_FILE
log "网站文件备份完成"

# 4. 同步到远程服务器
log "同步到远程服务器..."
rsync -az $BACKUP_DIR/*_$DATE.* $REMOTE_SERVER:$REMOTE_DIR/ 2>>$LOG_FILE
log "远程同步完成"

# 5. 清理旧备份
log "清理超过${RETENTION_DAYS}天的旧备份..."
find $BACKUP_DIR -name "*_*" -mtime +$RETENTION_DAYS -delete 2>>$LOG_FILE
log "旧备份清理完成"

log "========== 备份完成 =========="

18.11.6 备份验证

备份了不等于备份成功了!一定要验证备份的完整性。就像买了保险要确认保单有效一样。

bash 复制代码
# 验证tar备份
tar -tzvf /backup/full_20240115.tar.gz > /dev/null 2>&1
if [ $? -eq 0 ]; then
    echo "备份文件完整"
else
    echo "备份文件损坏!"
fi

# 验证压缩文件
gzip -t /backup/full_20240115.tar.gz
# -t表示测试,如果没输出说明文件完好

# 验证数据库备份
# 检查SQL文件末尾是否有完成标记
tail -5 /backup/mysql_20240115.sql
# 应该看到类似:-- Dump completed on ...

# 定期做恢复测试
# 最好每月做一次完整的恢复演练
# 在测试环境中恢复备份,验证数据是否正确

18.11.7 数据恢复

当数据丢失时,如何恢复:

bash 复制代码
# 从tar备份恢复
tar -xzvf /backup/full_20240115.tar.gz -C /restore/

# 从rsync备份恢复
rsync -avz /backup/home/ /home/

# 从数据库备份恢复
mysql -u root -p < /backup/mysql_20240115.sql

# 误删文件恢复(如果文件系统支持)
# ext4文件系统可以使用extundelete
extundelete /dev/sda1 --restore-file path/to/deleted/file

# 如果文件被rm删除但进程还持有文件句柄
# 参考18.7.4节的方法,通过/proc恢复

# 专业数据恢复工具:testdisk
# 可以恢复被删除的分区
testdisk /dev/sda

18.11.8 小结

数据备份与恢复的核心要点:

  1. 遵循3-2-1法则:3份数据、2种介质、1份异地
  2. 完全+增量/差异备份组合使用
  3. tar适合小量备份,rsync适合大量增量同步,dd适合磁盘级备份
  4. 自动化备份脚本 + crontab实现无人值守
  5. 必须验证备份完整性
  6. 定期做恢复演练

18.12 灾难恢复演练

18.12.1 为什么要做演练?------用"消防演习"来类比

你可能经常看到新闻报道"某公司服务器宕机,数据全部丢失",然后你心想:"这家公司怎么没有备份?"但现实是,很多公司确实有备份,但出了事还是恢复不了------因为他们的备份从来没测试过!

这就像学校每学期都要做消防演习一样。如果你从来没跑过消防通道,万一真的着火了,你能在浓烟中找到正确的路吗?

灾难恢复演练就是给你的系统做"消防演习"------模拟真实的灾难场景,验证你的备份和恢复方案是否真的有效。

没有做过演练的备份 = 没有备份!

18.12.2 灾难恢复计划的制定

在开始演练之前,必须先有一份完整的灾难恢复计划(DRP, Disaster Recovery Plan)。

复制代码
灾难恢复计划模板:
==============================

一、组织架构
- 恢复团队负责人:张三(手机:138xxxx)
- 系统管理员:李四(手机:139xxxx)
- 数据库管理员:王五(手机:137xxxx)
- 网络管理员:赵六(手机:136xxxx)
- 通讯协调人:钱七(手机:135xxxx)

二、系统分类与恢复优先级
P0级别(必须最先恢复):
  - 核心数据库
  - 主网站
P1级别(2小时内恢复):
  - 用户认证系统
  - 支付系统
P2级别(8小时内恢复):
  - 内部管理系统
  - 报表系统
P3级别(24小时内恢复):
  - 日志系统
  - 监控系统

三、RTO和RPO目标
- RTO(Recovery Time Objective)恢复时间目标:核心系统4小时内恢复
- RPO(Recovery Point Objective)恢复点目标:数据丢失不超过1小时

四、备份策略
- 核心数据库:每天完全备份 + 每小时增量备份
- 网站文件:每天rsync同步
- 配置文件:每次修改后立即备份
- 异地备份:每天同步到远程机房

五、恢复步骤
(针对每种故障场景,写出详细的恢复步骤)

六、联系方式
- 硬件供应商:400-xxx-xxxx
- 云服务商:400-xxx-xxxx
- ISP运营商:10000

==============================

18.12.3 演练场景设计

场景一:单台服务器故障

模拟:一台Web服务器突然宕机,硬件损坏无法修复。

演练步骤

bash 复制代码
# 1. 通知相关人员
# "演练开始:Web服务器web01硬件故障,需要在新服务器上恢复"

# 2. 准备新服务器
# 安装相同版本的操作系统

# 3. 恢复配置文件
scp backup@backup-server:/backup/etc_20240115.tar.gz /tmp/
tar -xzvf /tmp/etc_20240115.tar.gz -C /tmp/restore/
cp /tmp/restore/etc/nginx/nginx.conf /etc/nginx/
cp -r /tmp/restore/etc/nginx/conf.d/* /etc/nginx/conf.d/

# 4. 恢复网站文件
rsync -avz backup@backup-server:/backup/www_20240115/ /var/www/

# 5. 恢复数据库
gunzip < backup@backup-server:/backup/mysql_20240115.sql.gz | mysql -u root -p

# 6. 安装和配置服务
yum install nginx php-fpm mysql-server
systemctl start nginx php-fpm mysql

# 7. 验证
curl -I http://localhost
# 确保返回200

# 8. 修改DNS指向新服务器
# 或者修改负载均衡配置

# 9. 记录恢复时间
# 从开始到恢复完成用了多长时间?是否满足RTO?

# 10. 演练总结
# 哪些环节顺利?哪些环节有问题?需要改进什么?
场景二:数据库误操作

模拟 :DBA误执行了 DELETE FROM users(没有WHERE条件),删除了所有用户数据。

演练步骤

bash 复制代码
# 1. 立即停止应用,防止更多操作
systemctl stop webapp

# 2. 记录误操作的时间
# 假设误操作发生在 2024-01-15 14:30:00

# 3. 查看可用的备份
ls -la /backup/mysql/
# mysql_20240115.sql.gz  -- 今天的完全备份(凌晨2点)
# mysql_incr_14.sql.gz   -- 14点的增量备份

# 4. 方案A:从备份恢复
# 恢复凌晨2点的完全备份
gunzip < /backup/mysql_20240115.sql.gz | mysql -u root -p mydb

# 恢复增量备份到误操作前
gunzip < /backup/mysql_incr_14.sql.gz | mysql -u root -p mydb

# 问题:会丢失14点到14:30之间的数据

# 5. 方案B:使用MySQL binlog恢复
# 查看binlog
mysqlbinlog --start-datetime="2024-01-15 02:00:00" \
            --stop-datetime="2024-01-15 14:29:59" \
            /var/lib/mysql/mysql-bin.000123 | mysql -u root -p mydb

# 这样可以恢复到误操作前一秒的状态

# 6. 验证数据
mysql -u root -p -e "SELECT COUNT(*) FROM users"
# 确认数据恢复正确

# 7. 重启应用
systemctl start webapp

# 8. 记录RPO
# 数据丢失了多少?是否满足RPO要求?
场景三:整个机房故障

模拟:机房停电且UPS故障,所有服务器宕机。

演练步骤

bash 复制代码
# 这是最极端的灾难场景,需要异地灾备

# 1. 启用异地灾备机房
# 2. 修改DNS指向灾备机房
# 3. 从异地备份恢复数据
# 4. 验证所有服务
# 5. 通知用户

# 详细步骤取决于具体的灾备架构(冷备/热备/双活)

# 冷备:灾备机房只有备份,需要从头恢复
# 热备:灾备机房有实时数据同步,可以快速切换
# 双活:两个机房同时运行,一个挂了另一个自动接管

18.12.4 演练评估与改进

每次演练后,必须进行评估和改进。这就像消防演习后要开会总结一样。

复制代码
演练评估报告模板:
==============================

演练名称:Web服务器故障恢复演练
演练日期:2024-01-20
演练时长:3小时

一、演练目标
- 验证Web服务器在硬件故障后能在4小时内恢复(RTO=4h)
- 验证数据丢失不超过1小时(RPO=1h)

二、演练结果
- RTO实际值:3小时15分钟 ✓ 达标
- RPO实际值:30分钟 ✓ 达标

三、发现的问题
1. 备份文件下载太慢(花了30分钟)
   原因:备份服务器带宽不够
   改进:升级备份服务器带宽或使用专用备份网络

2. 恢复步骤文档不够详细
   原因:某些步骤依赖个人经验
   改进:完善恢复文档,做到任何人按文档操作都能完成

3. 新服务器环境与旧服务器不一致
   原因:系统版本和依赖包版本有差异
   改进:使用自动化部署工具(Ansible/Docker)保证环境一致性

4. 没有监控告警
   原因:恢复过程中没有监控,不知道是否成功
   改进:在恢复过程中加入健康检查

四、改进计划
1. 2月底前升级备份服务器带宽
2. 1月底前完善恢复文档
3. 3月底前实现自动化部署
4. 持续改进

==============================

18.12.5 自动化恢复------使用脚本和工具

bash 复制代码
#!/bin/bash
# 自动化恢复脚本示例
# 保存为 /usr/local/bin/auto_recover.sh

# ===== 配置 =====
BACKUP_SERVER="backup@example.com"
BACKUP_DIR="/remote_backup"
WEB_ROOT="/var/www"
DB_NAME="mydb"
DB_USER="root"
DB_PASS="password"

# ===== 恢复函数 =====

recover_web() {
    echo "恢复网站文件..."
    # 找到最新的网站备份
    LATEST_WWW=$(ssh $BACKUP_SERVER "ls -t $BACKUP_DIR/www_* | head -1")
    # 恢复
    rsync -avz $BACKUP_SERVER:$LATEST_WWW/ $WEB_ROOT/
    echo "网站文件恢复完成"
}

recover_db() {
    echo "恢复数据库..."
    # 找到最新的数据库备份
    LATEST_DB=$(ssh $BACKUP_SERVER "ls -t $BACKUP_DIR/mysql_*.sql.gz | head -1")
    # 恢复
    ssh $BACKUP_SERVER "gunzip -c $LATEST_DB" | mysql -u $DB_USER -p$DB_PASS $DB_NAME
    echo "数据库恢复完成"
}

recover_config() {
    echo "恢复配置文件..."
    LATEST_ETC=$(ssh $BACKUP_SERVER "ls -t $BACKUP_DIR/etc_*.tar.gz | head -1")
    scp $BACKUP_SERVER:$LATEST_ETC /tmp/
    tar -xzvf /tmp/$(basename $LATEST_ETC) -C /tmp/restore/
    cp /tmp/restore/etc/nginx/nginx.conf /etc/nginx/
    echo "配置文件恢复完成"
}

health_check() {
    echo "健康检查..."
    # 检查Nginx
    if systemctl is-active --quiet nginx; then
        echo "Nginx: 正常"
    else
        echo "Nginx: 异常"
        return 1
    fi
    
    # 检查PHP-FPM
    if systemctl is-active --quiet php-fpm; then
        echo "PHP-FPM: 正常"
    else
        echo "PHP-FPM: 异常"
        return 1
    fi
    
    # 检查MySQL
    if systemctl is-active --quiet mysqld; then
        echo "MySQL: 正常"
    else
        echo "MySQL: 异常"
        return 1
    fi
    
    # 检查Web访问
    HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost)
    if [ "$HTTP_CODE" = "200" ]; then
        echo "Web访问: 正常"
    else
        echo "Web访问: 异常 (HTTP $HTTP_CODE)"
        return 1
    fi
    
    return 0
}

# ===== 主流程 =====
START_TIME=$(date +%s)

echo "========================================="
echo "自动化恢复开始: $(date)"
echo "========================================="

recover_config
recover_db
recover_web

# 重启服务
systemctl restart mysqld php-fpm nginx

# 健康检查
if health_check; then
    END_TIME=$(date +%s)
    DURATION=$((END_TIME - START_TIME))
    echo "========================================="
    echo "恢复成功!耗时: ${DURATION}秒"
    echo "========================================="
else
    echo "========================================="
    echo "恢复失败!请检查日志"
    echo "========================================="
    exit 1
fi

18.12.6 演练频率建议

演练类型 频率 参与人员 说明
桌面推演 每月 运维团队 只在纸面上模拟,不实际操作
单系统恢复 每季度 运维团队 选一个系统实际恢复
全链路恢复 每半年 全体人员 模拟完整灾难场景
机房级演练 每年 全体人员+管理层 模拟机房整体故障

18.12.7 小结

灾难恢复演练的核心要点:

  1. 没做过演练的备份等于没有备份
  2. 制定完整的灾难恢复计划(DRP),包括RTO和RPO目标
  3. 设计真实的演练场景(单机故障、数据误删、机房故障)
  4. 每次演练后评估并改进
  5. 尽可能自动化恢复流程
  6. 定期演练,形成制度

全章总结

核心知识回顾

恭喜你读完了整章内容!让我们回顾一下本章的核心知识点:

18.1 故障排查方法论:像医生看病一样系统化排查,先收集信息再动手,一次只改一个地方,先备份再修改,分层排查。

18.2 系统无法启动排查:理解BIOS→GRUB→内核→systemd的启动流程,逐段排查。Live USB + chroot是万能修复手段。

18.3 文件系统损坏修复:fsck是主力工具,修复前必须卸载。超级块损坏可以用备份恢复。预防为主:UPS+日志文件系统+定期检查。

18.4 网络故障排查:分层排查(链路→网络→传输→应用)。ping/traceroute/mtr/ss/tcpdump是网络排障五件套。别忘了防火墙和SELinux。

18.5 性能问题诊断:四大瓶颈(CPU/内存/磁盘I/O/网络)。top看整体,vmstat看细节,iostat看磁盘,sar看历史。

18.6 内存泄漏排查:内存只增不减是典型表现。OOM Killer会自动杀进程。C/C++用valgrind,Java用jmap,Python用tracemalloc。

18.7 磁盘空间不足处理:df看整体,du看目录,find找大文件。删了文件但空间没释放用lsof查。inode耗尽用df -i查。

18.8 进程僵死与处理:僵尸进程杀父进程来清理,D状态进程要修复底层I/O。kill -15优雅退出,kill -9强制杀掉。

18.9 系统日志分析技巧:日志是破案的关键证据。journalctl是systemd日志的瑞士军刀。grep+awk+sort+uniq是日志分析黄金组合。

18.10 单用户模式与救援模式:单用户模式是"急诊室"。通过GRUB编辑进入,Live USB用于系统完全无法启动时。chroot是修复核心操作。

18.11 数据备份与恢复策略:3-2-1法则(3份数据/2种介质/1份异地)。tar/rsync/dd/mysqldump是四大备份工具。必须验证备份完整性。

18.12 灾难恢复演练:没做过演练的备份等于没有备份。制定DRP,设计真实场景,定期演练,持续改进。

故障排查速查表

最后,送你一份"故障排查速查表",遇到问题时可以快速查阅:

复制代码
================= 故障排查速查表 =================

【系统无法启动】
1. 有显示吗? → 无:检查电源/硬件 → 有:继续
2. 有GRUB菜单吗? → 无:修复GRUB → 有:继续
3. 内核能加载吗? → 卡住/Kernel Panic:检查root参数/initramfs
4. 能到登录界面吗? → 不能:用systemctl --failed查服务
5. 终极武器:Live USB + chroot修复

【文件系统损坏】
1. fsck检查 → umount后执行fsck -y /dev/sdX
2. 超级块损坏 → mke2fs -n查备份位置,e2fsck -b恢复
3. 变只读了 → mount -o remount,rw /  或 fsck修复

【网络不通】
1. 网线/网卡:ethtool / ip link show
2. IP配置:ip addr show
3. 网关:ping 网关IP
4. DNS:nslookup 域名
5. 端口:telnet/nc 目标IP 端口
6. 防火墙:iptables -L / firewall-cmd --list-all
7. 抓包:tcpdump -i eth0

【系统变慢】
1. top看负载和CPU/内存大户
2. vmstat看swpd/wa/r列
3. iostat看%util和await
4. 流程:内存不足→swap→磁盘I/O飙高→系统变慢

【内存泄漏】
1. ps/pidstat监控RSS是否持续增长
2. C/C++:valgrind --leak-check=full
3. Java:jmap -histo + jstat -gcutil
4. OOM:dmesg | grep oom

【磁盘满了】
1. df -h看哪个分区满
2. du -sh /*找大目录
3. find / -size +1G找大文件
4. 删了没释放?lsof | grep deleted
5. inode满了?df -i检查

【进程问题】
1. 僵尸进程:杀父进程 kill -CHLD PPID 或 kill PPID
2. D状态进程:修复I/O问题,无法kill
3. 优雅退出:kill PID 或 kill -15 PID
4. 强制杀掉:kill -9 PID

【日志查看】
1. 系统日志:journalctl -u 服务名 --since "1 hour ago"
2. 错误过滤:journalctl -p err
3. 上次启动:journalctl -b -1
4. 实时监控:journalctl -f 或 tail -f

【数据恢复】
1. 有备份:从备份恢复
2. 误删文件被进程占用:/proc/PID/fd恢复
3. 数据库:mysqlbinlog按时间点恢复
4. 终极方案:专业数据恢复工具testdisk/extundelete

===================================================

写在最后

恭喜你完成了第18章的学习!这一章是整本书中最长、也最实用的一章。

故障排查与恢复是一项需要大量实践的技能。仅仅读懂本章内容是不够的,你需要在日常工作中不断积累经验。这里给初学者几条建议:

  1. 在自己的虚拟机上多做实验。故意制造一些故障(比如填满磁盘、杀掉关键进程、修改错误的配置),然后尝试自己排查和修复。虚拟机的好处是随时可以快照恢复,不怕搞坏。

  2. 养成查看日志的习惯。每次遇到问题,第一反应应该是去看日志,而不是去搜索引擎。日志中往往有最直接的线索。

  3. 记录每一次排障过程。不管问题大小,都记录下来。时间长了,你就拥有了一本属于自己的"运维手册"。

  4. 模拟灾难,练习恢复。定期在自己的环境中模拟数据丢失、系统崩溃等场景,练习恢复操作。只有练过了,真出事时才不会慌。

  5. 备份!备份!备份! 重要的事情说三遍。再好的故障排查技术,都不如有一份可靠的备份。记住:数据是无价的,硬盘是有价的。

希望这一章能成为你Linux运维之路上的"急救手册"。当系统出故障时,翻开它,冷静分析,你一定能找到解决方案。

"最好的故障排查,是预防故障的发生。"

愿你的服务器永远稳定运行,愿你永远用不上这一章的知识------但万一需要,你已经准备好了。

相关推荐
weixin_431600442 小时前
NestJS 入门(3):Guard 如何挡住未登录请求?
前端·后端·学习·nest.js
带娃的IT创业者2 小时前
DeepTutor:当 Agent-Native 架构撞上个性化学习的临界点
学习·架构·ai agent·大模型应用·个性化学习·教育技术·agent-native架构
寺中人2 小时前
Linux 基础命令入门实战教程:从零掌握常用操作,新手快速上手
linux·运维·服务器·shell·linux 命令·linux 基础教程·linux 入门
xian_wwq2 小时前
【学习笔记】Context Engineering,AI Agent 真正的内存管理-4/16
笔记·学习·context
小张成长计划..3 小时前
【Linux】18:基础IO
linux·运维·服务器
寒月小酒3 小时前
AnythingLLM 学习
学习
MIXLLRED3 小时前
解决——Ubuntu远程桌面指令更换网络
linux·网络·ubuntu
疯狂打码的少年4 小时前
【数据结构】队列:定义、顺序队列与链式队列
数据结构·笔记
啊哈一半醒4 小时前
Windows 虚拟机搭建 CentOS 全过程(2026 最新版 + 踩坑解决)
linux·windows·centos