题目与适用场景
凌晨 02:00,应用写入 /var 时开始收到 ENOSPC。主机数据如下:
$ df -B1 /var
Filesystem 1B-blocks Used Available Use% Mounted on
/dev/nvme0n1p3 214748364800 210453397504 0 100% /var
$ sudo du -x -B1 -s /var
126701535232 /var
$ df -i /var
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/nvme0n1p3 20000000 4200000 15800000 21% /var换算后,文件系统总量为 200 GiB,df 统计已用 196 GiB,du 只能遍历到 118 GiB,已用空间 相差 78 GiB。inode 使用率只有 21%,因此 inode 耗尽不是眼前的原因。应用以普通用户运行, 主机不能重启,在确认文件所有者和用途之前也不能删除任何数据。
这是一道 Linux 生产排障题。高质量回答不会从删除最大的可见文件开始,而会先证明两个工具 观察的是同一文件系统、同一挂载命名空间、同一时间窗口、同一单位和同一权限范围,再解释 哪些已分配空间可能不在可见目录树中,最后选择影响范围明确的恢复动作。
面试官在考察什么
第一项信号是正确的统计模型。df 向已挂载文件系统读取整体块统计;du 遍历有名字、可访问 的文件和目录,估算这些目录项对应的已分配块。二者统计的是相关但不同的集合。已取消链接却 仍被进程打开的文件,或被后续挂载遮住的底层目录,仍占用文件系统块,却无法从当前目录树 按名字遍历到。
第二项信号是控制统计范围。拿 df /var 与会跨文件系统的 du /var 比较、一个命令在容器内 运行、忽略权限错误,或在日志快速增长时先后采样,都会人为制造差异。候选人应该先统一挂载 目标、挂载命名空间、文件系统边界、字节单位、权限和观察窗口,再判断根因。
第三项信号是安全处置。lsof +aL1 /var 能找出该文件系统中链接数小于 1 的已打开文件,但 输出只是证据,不代表可以直接杀进程或截断描述符。强回答会确认服务负责人、文件描述符、 设备和增长状态,优先使用应用支持的日志重新打开操作或优雅重启,同时验证空间回收和应用健康。
第四项信号是拆开相似现象。ext4 预留块会减少普通进程可用的空间,使 Size - Used 大于 Avail;它不能解释为什么 df 统计的已用空间比同文件系统的 du 多 78 GiB。inode 耗尽 同样会返回 ENOSPC,但题目给出的 21% 已排除这条路径。写时复制快照、压缩和配额需要对应 文件系统的工具,不能照搬 ext4 命令。
最后一项信号是闭环。回答需要保留处置前后证据,说明回收的字节来自哪里,确认写入进程已经 重新打开正确路径,检查已删除但仍打开的文件是否继续增长,并改进日志轮转或挂载变化监控。 只看到 df 百分比下降,不能证明应用恢复或数据无损。
回答前要先确认的问题
- 两个命令是否在同一挂载命名空间运行? 如果一个在宿主机、另一个在容器或服务命名空间,
/var 可能对应不同挂载。排查应进入受影响进程的命名空间,或明确只使用宿主机视角。
- 究竟哪个路径写入失败,它属于哪个文件系统?
findmnt -T能把任意路径映射到挂载点。
嵌套挂载或 bind mount 会改变应该拿哪组 df 和 du 数据比较。
- 采样时间、单位和权限是否一致? 快速增长的日志会在两次命令之间变化;可读格式会舍入;
无法访问的目录会让普通用户执行的 du 少算。
- 涉及哪些文件系统和存储层? ext4、XFS、Btrfs、ZFS、overlay、LVM 快照和云盘快照的
统计与恢复手段不同。
- 耗尽的是数据块、inode 还是配额?
df -i、配额工具和应用报错决定分支。删除一个大
文件不能解决 inode 耗尽,全局有空闲块也不能绕过用户或项目配额。
- 哪些恢复动作获准执行? 重新打开日志、优雅重载、优雅重启、故障切换和短维护窗口的
可用性风险不同。紧急截断描述符需要明确负责人和回退方案。
- 需要在多久内恢复多少余量? 目标决定先限制写入、扩容、切流还是回收已确认的空间,
但不能成为删除未知数据的理由。
30 秒回答框架
“我会先停止破坏性操作,用 findmnt、df -B1 和 sudo du -x -B1 确认相同路径、文件系统、 挂载命名空间、时间、权限和字节单位。inode 只有 21%,所以我会量化 78 GiB 的块差额,并用 lsof +aL1 /var 查找已取消链接但仍被进程打开的文件。如果它能解释差额,我会核对 PID、 描述符、设备和服务负责人,然后走应用支持的日志重新打开或优雅重启,并确认空间返回。如果 不能解释,我会检查被挂载遮住的目录、权限遗漏和文件系统特有的元数据或快照。ext4 预留块 能解释普通进程可用空间为零,解释不了 78 GiB 的已用空间差额。最后我会复测原命令、应用 写入、日志和复发指标。”
分步骤深入解答
先让比较可以复现。尽量在接近的时间采集失败路径、当前命名空间、挂载来源、文件系统类型、 块统计、inode 统计和 du 总量:
readlink -f /var
findmnt -T /var -o SOURCE,FSTYPE,OPTIONS,TARGET
df -B1 /var
df -i /var
sudo du -x -B1 -s /var-x 限制 du 只统计 /var 所在文件系统,不把嵌套文件系统加进总数。-B1 消除块单位 歧义。应以足够权限运行 du 并阅读诊断信息;把权限错误静默丢弃,会把访问问题误判成存储 问题。命令还必须来自同一挂载命名空间。写入方在容器内时,应有意识地比较宿主机视角和 nsenter 视角,不能因为路径字符串相同就假设对象相同。
观察到的差额是一项排障指标,不是等着被找到的单个文件:
same_filesystem_gap = df_used_blocks - du_reachable_allocated_blocks
= 196 GiB - 118 GiB
= 78 GiB这个等式是近似分解:文件系统元数据、分配粒度、并发写入、写时复制共享、压缩和工具语义 不能组成严格无重叠的账本。但稳定的 78 GiB 差额不可能只靠显示舍入解释。此时应先找目录树 之外仍占块的对象,而不是继续找另一个普通大路径。
最有价值的检查是最终目录链接已经删除、进程却仍保持打开的文件:
sudo lsof +aL1 /var
# After selecting a candidate from lsof, verify the live descriptor.
sudo readlink /proc/2481/fd/7
sudo stat -Lc 'device=%d inode=%i size=%s blocks=%b block_size=%B' /proc/2481/fd/7Linux 的 unlink 删除的是名字。如果它是文件的最后一个链接,但进程仍保持文件打开,文件 和数据块要等最后一个相关描述符关闭后才释放。du 无法通过目录树找到该 inode,df 仍会 统计它的已分配块。常见事故路径是日志轮转错误:日志文件被删除或改名,写入进程却继续写旧 描述符。
不能把 lsof 的 SIZE/OFF 对所有稀疏文件或特殊文件都当作精确可回收量。先确认描述符指向 目标设备上的普通文件、链接数为零、由哪个进程持有、是否仍在增长。一个文件解释不了大部分 78 GiB 时,要继续关联多个大描述符。
推荐的恢复顺序是:应用专用的重新打开操作、优雅重载、优雅重启或故障切换,最后才是紧急 干预。受支持的日志重新打开能让进程关闭旧描述符并打开当前路径,不必终止整台主机。受控 服务重启同样能释放描述符,但要考虑副本、就绪状态和正在处理的工作。先发 SIGKILL 会绕过 清理路径。文件已经取消链接后,再执行 rm 也不会释放活动描述符。
通过 /proc/PID/fd/FD 写入可以在不停止进程时释放空间,但只能作为最后手段。写入方可能 保留旧偏移量,下一次写入形成稀疏空洞、破坏应用格式或打破内部不变量。只有在服务负责人 确认描述符和写入语义、流量已受控、证据已保留且有恢复方案后才能使用。通用运行手册不能把 截断描述符写成默认修复。
如果 lsof 解释不了差额,就检查挂载拓扑。把另一个文件系统挂载到非空目录上,会遮住底层 目录项,但这些底层文件仍占父文件系统空间。findmnt 能显示嵌套、bind 和重复覆盖的挂载。 经批准的维护操作中,可以把 /var 非递归 bind 到 /var 之外的空目录,在不中断线上子挂载 的情况下观察父文件系统底层视图:
sudo mkdir -p /mnt/var-underlay
sudo mount --bind /var /mnt/var-underlay
sudo du -x -B1 -s /mnt/var-underlay
sudo umount /mnt/var-underlay执行前必须结合真实挂载树验证;命名空间传播和平台策略可能改变结果。不能为了查看隐藏文件 直接卸载繁忙的生产文件系统。发现底层文件后,仍要先确认所有者和保留策略,再移动或删除。
接着把“普通进程可用空间”与“已用空间差额”分开。ext4 的特权进程预留块可能让应用看不到 原始空闲块。先检查,不要修改:
source=$(findmnt -n -o SOURCE -T /var)
sudo tune2fs -l "$source" | grep -E 'Block count|Reserved block count|Block size'
sudo du --inodes -x -d1 /var | sort -n题目中总量减已用仍有 4 GiB,但 Avail 为零,符合部分空闲块不向普通服务开放的表现。它能 解释眼前的写入失败,却不能解释 df 为什么比 du 多统计 78 GiB 已用空间。事故中降低 预留比例会拿走特权守护进程的保命余量,也可能增加碎片风险;这应是容量决策,不能条件反射 执行 tune2fs -m 0。
inode 耗尽是另一条独立的 ENOSPC 路径。本题 df -i 只有 21%,因此不是触发因素。如果 达到 100%,应使用 du --inodes -x 定位小文件密集目录,只清理有保留策略覆盖的数据。回收 数据块和回收 inode 是两个目标。
最后才进入文件系统特有的统计。GNU du 明确指出,写时复制共享、压缩、备份块和网络文件 系统都可能让它的估算与设备消耗不同。在 Btrfs 或 ZFS 上,应使用对应工具检查子卷或数据集、 快照、配额、独占字节和引用字节;在 overlay 存储上,要从拥有分层数据的命名空间和运行时 排查。不能对 XFS 或 Btrfs 设备运行 ext4 工具。
如果已打开文件、隐藏数据、访问错误、并发增长、预留可用量和文件系统特性都解释不了证据, 再检查内核日志和文件系统健康。修复工具不是在线排障捷径:先保留证据,遵循对应文件系统的 官方流程,需要卸载的检查应进入计划维护。
最后用同一组证据关闭事故。重新执行 df -B1、df -i 和有权限的 du -x -B1;确认回收 字节与已关闭或已清理的分配大致一致;验证新的应用写入;确认服务正在写正确的命名文件; 检查延迟、错误、副本和数据完整性。后续监控应同时覆盖块与 inode 余量、已删除但仍打开的 文件增长、日志轮转失败、挂载拓扑变化和意外快照增长。
高质量示范回答
“78 GiB 的差异是可能成立的,因为 df 和 du 的统计范围不同。df 读取文件系统整体块 统计,du 遍历它能访问的命名目录项。我会先用 findmnt 映射 /var,在受影响服务的挂载 命名空间中运行两个工具,统一字节单位、root 权限、du -x 和采样时间,先证明差额真实存在。 inode 只有 21%,这条分支可以暂时排除。
第一假设是已删除但仍打开的文件。我会执行 lsof +aL1 /var,再核对大结果的 PID、文件描述 符、设备、inode、负责人和增长情况。如果日志进程持有的空间接近差额,我会走它支持的日志 重新打开信号或受控优雅重启。服务负责人确认影响之前,我不会杀进程或截断 /proc。描述符 关闭后,我会确认 df 回收了预期范围,应用也在写新的命名日志。
如果仍解释不了,我会检查 findmnt 是否存在遮住底层文件的挂载,阅读 du 权限错误,再用 文件系统专用工具检查快照、写时复制分配、配额和元数据。当前设备是 ext4,所以还会检查预留 块。预留块可以解释为什么总量减已用还有 4 GiB、应用可用量却是零,但不能解释 78 GiB 的 已用空间差额。
最后我会复跑原始测量,测试失败过的写入链路,检查服务健康和数据完整性,并记录究竟释放了 哪部分分配。防复发工作是修正日志轮转或挂载生命周期,同时监控块、inode 和已取消链接却仍 打开的文件,避免文件系统再次进入紧急预留区。”
常见错误
- 立即删除最大的可见文件 → 它可能是必要数据,也可能与不可见分配无关 → **先统一统计
范围并确认所有者,再修改数据。**
- 拿
df /var与du /比较 → 嵌套文件系统和不同根目录会让减法失效 → **映射失败路径,
对同一文件系统使用 du -x。**
- 忽略
du权限错误 → 有名字但无法访问的文件会被少算 → **用足够权限执行并检查每条
诊断。**
- 在不同挂载命名空间运行命令 → 同一路径可能落到不同文件系统 → **在受影响进程的
命名空间采集两组数据。**
- 把 78 GiB 都叫作预留块 → ext4 预留影响普通用户可用量,差额比较的是已用块和可达分配
→ 分别计算两种差异。
- 对已删除但仍打开的文件继续执行
rm→ 文件名早已不存在,活动描述符仍持有 inode →
让所属进程关闭或安全重新打开描述符。
- 把
kill -9当成第一修复 → 突然终止可能丢工作并绕过清理 → **优先使用受支持的重新
打开、重载、优雅重启或故障切换。**
- 习惯性截断
/proc/PID/fd/FD→ 保留的偏移和格式假设可能制造新损坏 → **只在语义已经
验证的获批紧急情况下使用。**
- 为查看隐藏文件直接卸载繁忙子挂载 → 依赖服务可能立即故障 → **先检查拓扑,使用获批的
替代视图或维护窗口。**
- 把 inode 用量并入字节差额 → inode 耗尽是独立的
ENOSPC原因 → **用df -i单独
排查高文件数目录。**
- 对所有文件系统套 ext4 命令 → XFS、Btrfs、ZFS 和 overlay 的快照与分配语义不同 →
先确认 FSTYPE,再用对应工具。
- 看到
df降低就结束 → 写入进程可能仍不健康或写错目标 → **验证应用写入、命名文件、
数据完整性和复发指标。**
追问与回答
追问 1:如果没有 lsof,或者它看不到目标进程怎么办?
在与写入方相同的 PID 和挂载命名空间检查 /proc/PID/fd,查找链接目标以 (deleted) 结尾的 描述符,再核对设备、inode、链接数和进程所有者。权限、PID 命名空间或安全策略可能让宿主机 lsof 漏掉容器进程。下一步仍不能自动变成截断 /proc,恢复决策必须按服务语义制定。
追问 2:如果 du 反而比 df 大呢?
先确认 du 是否遍历了嵌套文件系统;du -x 可以排除这一点。再检查硬链接参数语义、 apparent-size 选项、并发删除,以及写时复制或压缩统计。du 估算指定目录层次,df 统计一个 文件系统,差异方向不同,优先怀疑的范围错误也不同。
追问 3:换成 Btrfs 或 ZFS 后有什么变化?
可见文件大小不再足够,因为快照和写时复制共享可能在路径删除后继续保留块。要使用对应文件 系统的空间、子卷或数据集、快照和配额视图,区分引用分配与独占分配;只有在明确保留策略下 才能删除快照。ext4 预留块和 tune2fs 结论不能迁移过去。
追问 4:能否把 ext4 预留比例直接设为零救急?
只能在完成容量与可靠性评审后决定。预留空间让特权进程继续工作,也有助于控制碎片。降低 比例可能恢复普通用户可用量,却不会删除已用块,也不能解释未链接文件的占用。应优先回收或 扩展已确认的容量,再根据卷用途和运维策略调整预留。
追问 5:怎样安全复现已删除但仍打开的行为?
使用一次性文件系统或测试主机:让进程打开一个大测试文件,在描述符仍打开时取消路径链接, 比较 df 与 du,通过 lsof +L1 观察,再关闭描述符并确认数据块返回。不能在生产卷制造 实验,也不能借用真实服务的描述符。
追问 6:长期告警应该监控什么?
按文件系统和命名空间监控块与 inode 的耗尽时间和最小余量。增加已删除但仍打开的普通文件、 日志轮转失败、快照增长和挂载拓扑变化的周期清单。告警应链接到从统一统计范围开始的运行 手册,任何破坏性恢复动作都需要数据或服务负责人批准。