Linux 基础体系 · 第 4/85 篇。示例面向现代主流 Linux 发行版;发行版差异、权限和生产风险会明确说明。
Linux 目录与文件系统层次:FHS、挂载点、proc、sysfs 与设备
Linux 中的“目录”并不都表示磁盘上的普通目录。/etc 通常来自根文件系统,/proc 通常由内核动态生成,/sys 由 sysfs 提供,/dev 则主要包含设备节点。它们在路径形式上都像目录,但数据来源、生命周期、权限模型和故障表现完全不同。
理解 Linux 文件系统层次,需要同时区分四个概念:
- FHS:约定目录应该承担什么职责。
- VFS:内核如何统一处理不同类型的文件系统和路径。
- 挂载点:如何把一个文件系统接入目录树。
- 伪文件系统与设备节点:为什么有些“文件”并不对应磁盘上的数据。
一、从目录树到 VFS:Linux 看到的不是“一个磁盘”
1.1 路径名只是统一接口
用户进程执行:
cat /etc/hostname
表面上是在读取一个路径。内核实际要完成的是:
- 从进程所属的 mount namespace 找到根挂载点
/。 - 逐级解析
etc。 - 在根文件系统中找到
hostname对应的目录项和 inode。 - 根据 inode 的文件系统类型,调用具体文件系统的读取操作。
- 将数据复制到用户空间。
VFS(Virtual File System,虚拟文件系统)提供统一抽象。ext4、XFS、Btrfs、tmpfs、proc、sysfs 等文件系统都通过 VFS 暴露类似的对象:
- superblock:描述一次文件系统实例及其操作。
- inode:描述文件对象的元数据和操作。
- dentry:缓存路径中的目录项名称与 inode 关系。
- file:一次
open()成功后形成的进程级打开文件对象,包含当前偏移、打开标志等。 - mount:把某个文件系统实例接到目录树中的关系。
因此,“路径属于哪个磁盘”不能只看路径字符串。必须考虑当前 mount namespace 和挂载表。
1.2 一个目录可以被另一个文件系统覆盖
假设根文件系统中原本有目录:
/
└── data/
└── old.txt
将另一个文件系统挂载到 /data:
mount /dev/sdb1 /data
之后路径访问结果变为:
/
└── data/ # 现在看到的是 /dev/sdb1 的根目录
/data/old.txt 不一定被删除;它仍然存在于根文件系统中,只是被挂载点遮蔽了。卸载后:
umount /data
原来的 old.txt 才重新可见。
这会导致一个重要故障:管理员把文件写入了 /data,后来挂载磁盘;磁盘挂载后看不到这些文件,以为数据丢失。实际数据可能仍在底层文件系统,只是被覆盖显示。
可以用以下命令观察挂载关系:
findmnt /
findmnt /data
df -T /data
df 查询的是路径当前落到的文件系统;如果 /data 已经被单独挂载,结果通常不会显示根文件系统。
二、FHS:目录名称背后的职责约定
FHS(Filesystem Hierarchy Standard,文件系统层次结构标准)规定 Linux 类 Unix 系统中常见目录的用途。它主要是布局约定,不是内核强制执行的目录清单。
不同发行版可能:
- 将
/bin、/sbin、/lib设计为指向/usr下目录的符号链接,即所谓/usr合并布局。 - 把服务运行时数据放在
/run。 - 采用独立的
/boot、/home、/var、/tmp文件系统,也可能全部放在根文件系统中。 - 对
/opt、/srv、/usr/local的具体使用方式不同。
因此,FHS 说明“应该表达什么语义”,不保证每个发行版的实现方式完全相同。
2.1 根目录 /
/ 是进程看到的文件系统树根。它不一定等于某个磁盘分区的全部内容:
- 可能是一个独立的 ext4 或 XFS 文件系统。
- 可能是 initramfs 解包后的临时根,随后切换到真正的根文件系统。
- 可能来自 LVM、RAID、网络文件系统、加密映射或虚拟机镜像。
- 在容器中,
/可能是镜像层和多个挂载点组合出的根。
根目录下常见目录如下:
| 路径 | 主要职责 |
|---|---|
/bin |
供所有用户使用的基本命令;现代系统中常可能链接到 /usr/bin |
/sbin |
系统管理命令;现代系统中常可能链接到 /usr/sbin |
/lib、/lib64 |
基本共享库、动态加载器和内核模块等;具体布局随架构和发行版变化 |
/boot |
启动加载器配置、内核、initramfs 等启动所需文件 |
/dev |
设备节点和若干伪设备 |
/etc |
主机范围的系统配置文件 |
/home |
普通用户的家目录 |
/root |
root 用户的家目录 |
/media |
通常用于可移动介质的自动挂载位置 |
/mnt |
管理员临时挂载文件系统的传统位置 |
/opt |
附加应用软件及其静态数据 |
/proc |
proc 伪文件系统,暴露进程和内核信息 |
/run |
自启动以来的运行时状态数据 |
/srv |
本机对外提供服务的数据 |
/sys |
sysfs,暴露设备、驱动、内核对象和属性 |
/tmp |
临时文件;清理规则和权限由系统实现决定 |
/usr |
大部分用户空间程序、库、共享数据和文档 |
/var |
会变化的数据,例如日志、缓存、队列、数据库状态 |
这些目录的区别不仅是“文件放在哪里”,还包括数据的生命周期。
例如:
/etc通常需要持久化并纳入配置备份。/run通常在重启后重新生成,不应被当作永久配置目录。/var/lib常保存服务状态,不能像缓存一样随意删除。/var/cache通常可重建,但具体应用仍可能有自己的约束。/tmp适合临时文件,不适合保存重启后必须存在的数据。
2.2 /usr 与 /var 的区别
/usr 不是“用户家目录”的缩写。它主要保存相对静态的用户空间软件:
/usr/bin
/usr/sbin
/usr/lib
/usr/share
/local
/var 保存运行过程中变化的数据:
/var/log 日志
/var/lib 服务持久状态
/var/cache 可重建缓存
/var/spool 队列和待处理任务
/var/tmp 比 /tmp 更倾向于跨重启保留的临时文件
例如,软件包管理器的安装文件通常在 /usr,软件包数据库和下载缓存可能在 /var/lib、/var/cache。只读挂载 /usr 在某些系统设计中可行,但将 /var 也当作静态目录会破坏服务运行状态。
2.3 /etc 不是所有配置的唯一位置
/etc 主要保存主机范围配置,但实际配置可能来自多个来源:
/etc/passwd等传统配置文件。/etc/systemd/system中的管理员服务单元。/run中由运行时生成的服务状态。/usr/lib/systemd/system中由软件包提供的默认服务单元。- 环境变量、命令行参数或远程配置系统。
因此,修改一个服务时,不能仅凭“配置文件应该在 /etc”就认为找到的第一个文件一定是最终生效配置。
三、挂载点:把文件系统接入目录树
3.1 挂载的形式化模型
可以把一个挂载关系抽象为:
(source filesystem, mount options) -> (directory in a mount namespace)
例如:
mount -t proc proc /proc
其含义不是把名为 proc 的普通文件复制到 /proc,而是:
- 创建或取得一个 proc 文件系统实例。
- 将它接到当前 mount namespace 中的
/proc目录。 - 后续路径解析遇到
/proc时,转入该文件系统。
设备文件系统的挂载类似:
mount -t tmpfs tmpfs /mnt
这里的 tmpfs 是文件系统类型,数据主要由内核以内存和交换空间形式管理,并不对应一个传统磁盘分区。
3.2 挂载点必须满足什么条件
常见前置条件包括:
- 目标目录已经存在。
- 目标目录通常应为空,避免遮蔽原有内容。
- 执行者拥有
CAP_SYS_ADMIN,通常是 root 或具备相应能力的进程。 - 文件系统类型已被内核支持。
- 源设备、远程导出或伪文件系统参数有效。
示例:
sudo mkdir -p /mnt/test
sudo mount -t tmpfs -o size=64M tmpfs /mnt/test
findmnt /mnt/test
df -hT /mnt/test
预期会看到文件系统类型为 tmpfs,大小约为 64 MiB。这个大小是限制或上限语义,不代表挂载时立刻分配了 64 MiB 的物理内存。
验证完成后卸载:
sudo umount /mnt/test
如果当前 shell 的工作目录位于 /mnt/test,或者某个进程持有其中的打开文件,卸载可能失败并返回 target is busy。可以诊断:
findmnt -R /mnt/test
sudo fuser -vm /mnt/test
sudo lsof +D /mnt/test
lsof +D 可能需要遍历大量目录,生产环境中应注意开销。umount -l 是 lazy unmount,会先从目录树中摘除挂载关系,等现有引用释放后清理;它不是强制终止进程,也不应被当作普通卸载失败时的无条件替代品。
3.3 /etc/fstab 与启动挂载
/etc/fstab 描述系统启动或显式调用 mount -a 时应尝试建立的挂载关系。典型格式为:
UUID=xxxx-xxxx /data ext4 defaults,nofail 0 2
字段依次表示:
- 源,可以是设备、UUID、LABEL、网络路径或伪文件系统名。
- 目标挂载点。
- 文件系统类型。
- 挂载选项。
dump相关字段,现代系统通常为0。- 文件系统检查顺序,根文件系统通常优先。
使用 UUID 而不是 /dev/sdb1,是为了避免设备名因硬件枚举顺序变化而改变。nofail 可以避免某些非关键盘不可用时阻塞启动,但也可能掩盖本应被发现的存储故障。添加或修改配置后,可先验证:
sudo mount -av
这会尝试挂载 /etc/fstab 中尚未挂载的条目,并显示结果。修改前应保留备份;根文件系统或 /etc 配置错误可能使系统无法正常启动。
3.4 挂载命名空间
mount namespace 决定一个进程看到哪些挂载点。容器通常拥有与宿主机不同的 mount namespace,因此:
宿主机 /proc
容器内 /proc
可能不是同一个挂载实例或具有相同的可见内容。
这解释了两个现象:
- 在容器内执行
mount,看到的挂载列表可能比宿主机少。 - 在容器内创建或卸载挂载,不一定影响宿主机;但共享挂载传播配置可能使变化传播到其他 namespace。
/proc/self/mountinfo 比传统 /proc/mounts 提供更多 namespace、挂载 ID、父子关系和传播信息:
cat /proc/self/mountinfo
findmnt --vfs
挂载传播模式包括 shared、private、slave 等。它们决定一个 namespace 中的 mount 变化是否传播到另一个 namespace。容器运行时若错误设置传播属性,可能导致宿主机意外看到容器挂载,或者容器看不到宿主机后来新增的挂载。
四、普通文件系统与伪文件系统
4.1 普通文件系统
ext4、XFS、Btrfs 等通常把文件内容和元数据持久化到块设备或其上层映射设备:
磁盘分区 / LVM / RAID
↓
文件系统
↓
VFS
↓
目录路径
“文件大小”通常是逻辑大小,不一定等于实际占用的磁盘空间。稀疏文件就是典型例子:
truncate -s 1G /tmp/sparse
ls -lh /tmp/sparse
du -h /tmp/sparse
可能看到:
ls: 1.0G
du: 0 # 或非常小
truncate 只设置逻辑文件长度,没有写入其中的块。访问未分配区域时,文件系统返回逻辑上的零;第一次写入某个区域时才可能分配实际块。
4.2 伪文件系统
伪文件系统通常由内核或用户空间服务动态提供:
proc:进程、内核和部分系统状态。sysfs:设备、驱动、总线、内核对象及属性。tmpfs:以内存为主要存储的普通目录树。devtmpfs:内核维护的设备节点文件系统。debugfs:调试接口,通常需要显式挂载,接口不保证稳定。
伪文件系统不意味着“完全没有内存占用”,也不意味着所有内容都不能写。准确含义是:它们的数据语义不是传统磁盘文件系统的持久化文件内容。
五、proc:以文件形式暴露进程和内核状态
5.1 proc 的核心对象
proc 文件系统通常挂载在 /proc:
findmnt /proc
常见结果类似:
TARGET SOURCE FSTYPE OPTIONS
/proc proc proc rw,nosuid,nodev,noexec,relatime
/proc 下的数字目录代表进程 ID:
/proc/1
/proc/1234
/proc/self
/proc/thread-self
其中:
/proc/1表示 PID 1。/proc/self是指向“访问它的当前进程”的特殊路径。/proc/thread-self更精确地指向当前线程对应的 proc 路径。
因此,不同进程读取相同字符串 /proc/self/status,得到的可能是不同内容。
查看当前 shell 的状态:
cat /proc/self/status
其中常见字段包括:
Name:
Pid:
PPid:
Uid:
Gid:
VmSize:
VmRSS:
Threads:
VmRSS 是驻留集大小的一个统计值,但它不是精确的“该进程独占物理内存”;共享页、内核记账和统计时机都会影响解释。内存诊断不能只看一个字段。
5.2 /proc 中的几类信息
进程信息
/proc/<pid>/cmdline
/proc/<pid>/environ
/proc/<pid>/fd/
/proc/<pid>/maps
/proc/<pid>/mountinfo
/proc/<pid>/status
读取权限受普通文件权限、进程关系、ptrace 访问控制和挂载选项影响。例如:
ls -l /proc/$$/fd
readlink /proc/$$/fd/0
/proc/<pid>/fd/3 通常是指向进程已打开文件的符号链接,但它不是普通磁盘符号链接;内核在解析时根据文件描述符表动态返回目标。
全局内核状态
/proc/cpuinfo
/proc/meminfo
/proc/loadavg
/proc/uptime
/proc/stat
/proc/filesystems
例如:
grep -E '^(MemTotal|MemAvailable|SwapTotal|SwapFree):' /proc/meminfo
MemAvailable 是内核估计“无需交换即可供新应用使用”的内存量,不等于简单的 MemFree。直接把 MemFree 当作剩余可用内存是常见误读。
内核参数
/proc/sys/
例如:
sysctl vm.swappiness
cat /proc/sys/vm/swappiness
很多 /proc/sys 参数可写:
sudo sysctl -w net.ipv4.ip_forward=1
这类修改通常只影响当前运行内核;要持久化,发行版通常通过 /etc/sysctl.conf 或 /etc/sysctl.d/*.conf 管理。错误修改可能影响网络转发、内存回收、文件描述符上限或安全边界。写入前应确认参数类型、作用域和回滚方法。
5.3 proc 的访问控制与 PID namespace
proc 中的进程目录与进程可见性受 PID namespace 影响。容器内的 PID 1 可能对应宿主机中的另一个 PID,容器内通常只能直接看到本 namespace 可见的进程。
某些系统使用 hidepid 限制进程信息暴露,例如:
findmnt -no OPTIONS /proc
可能包含:
hidepid=2
这会减少普通用户查看其他进程信息的能力。/proc 不是一个静态目录快照;进程创建、退出、线程变化和内核状态改变都会影响读取结果,因此并发读取时必须接受内容可能在读取过程中变化。
六、sysfs:内核设备模型的目录化表示
6.1 sysfs 与 proc 的职责不同
/proc 早期承载了大量不同类型的内核信息;sysfs 则主要围绕 Linux 设备模型组织:
- 设备(device)
- 驱动(driver)
- 总线(bus)
- 类(class)
- 内核对象(kobject)
- 属性(attribute)
- 链接关系
查看挂载:
findmnt /sys
典型结果会显示文件系统类型为 sysfs。
sysfs 中看起来像普通文件的属性,通常由内核在读取时生成。例如:
cat /sys/class/net/eth0/operstate
cat /sys/class/net/eth0/mtu
这些内容不一定存在于某个磁盘 inode 中。写入属性时,内核会把文本转换为参数并调用对应内核对象的处理函数。
6.2 /sys/devices、/sys/class 和 /sys/block
/sys/devices
这是接近设备物理拓扑和内核设备模型的层次。设备可能按总线、控制器、端口等关系组织:
/sys/devices/
/sys/class
这是按功能类别提供的视图,例如网络接口、块设备、输入设备:
/sys/class/net/
/sys/class/block/
/sys/class/net/eth0 往往是指向 /sys/devices/... 的符号链接。它提供易于按类别查找的路径,而不是复制一份设备对象。
/sys/block
块设备的类别视图:
ls -l /sys/class/block
ls -l /sys/block
可能看到:
sda
sda1
nvme0n1
nvme0n1p1
dm-0
这些名称反映内核识别到的块设备,不等同于“实际物理磁盘”。例如:
dm-0可能是 LVM 逻辑卷、加密映射或其他 device-mapper 设备。md0可能是软件 RAID。loop0可能是一个文件映射出的循环设备。nvme0n1p1是 NVMe 设备上的分区。
6.3 属性不是任意可写配置文件
例如:
cat /sys/class/net/eth0/address
cat /sys/class/net/eth0/carrier
carrier 通常反映链路状态;它不是通过写入就能“制造网线连接”的普通配置文件。
某些属性确实允许写入,例如部分设备的电源管理或块设备队列参数:
cat /sys/block/sda/queue/scheduler
但可写属性依赖内核版本、驱动和设备。生产环境中不应根据“看到一个文件”就直接 echo value > file;应先确认:
- 文件是否明确支持写入。
- 写入值的格式和范围。
- 作用是临时运行时配置还是持久化配置。
- 设备是否正在使用。
- 修改失败后的恢复方式。
sysfs 的 ABI 中,一些用户可见属性会保持兼容,但并非所有调试接口都具有长期稳定保证。尤其是 debugfs,更适合调试而非自动化生产依赖。
七、设备:/dev 中的文件到底是什么
7.1 设备节点不是设备数据本身
Linux 将设备暴露为特殊文件,通常位于 /dev:
ls -l /dev/null /dev/tty /dev/sda
可能看到类似:
crw-rw-rw- 1 root root 1, 3 ... /dev/null
brw-rw---- 1 root disk 8, 0 ... /dev/sda
首字符表示类型:
c:字符设备(character device)b:块设备(block device)-:普通文件l:符号链接p:FIFOs:Unix socket
设备节点中的两个数字是 主设备号 和 次设备号:
主设备号:次设备号
内核据此把对设备节点的操作分派到相应设备驱动或设备子实例。设备节点本身只保存访问入口和元数据,不保存磁盘分区中的文件内容。
7.2 从 open("/dev/...") 到驱动
以读取块设备为例,简化数据流如下:
flowchart TD
A[用户进程 open/read] --> B[VFS 解析 /dev/sda]
B --> C[设备节点 inode]
C --> D[主次设备号]
D --> E[块设备层]
E --> F[设备映射层: 分区/LVM/RAID/加密等]
F --> G[具体驱动]
G --> H[控制器和硬件]
关键路径是:
- 路径解析找到
/dev/sda的设备节点。 open()识别该 inode 是块设备。- 内核根据主次设备号找到块设备对象。
- 请求经过分区、device-mapper、RAID 或加密层。
- 最终由驱动与控制器交互。
- 读到的数据返回到 VFS 和用户进程。
/dev/sda 因此不是“磁盘挂载后的目录”。只有当某个分区或逻辑设备上的文件系统被挂载后,才会出现可访问的目录树:
/dev/sda
└── /dev/sda1
└── mount 到 /data
7.3 字符设备与块设备
字符设备通常提供按字节或按流访问的接口,例如:
/dev/tty
/dev/null
/dev/random
块设备以块为基本访问单位,并支持缓存、随机访问和文件系统挂载,例如:
/dev/sda
/dev/nvme0n1
/dev/mapper/vg0-lv_data
这不是绝对的用户体验分类;具体行为由驱动定义。不能仅凭名称判断一个设备是否可安全读取或写入。
7.4 /dev 的来源:devtmpfs 与 udev
现代 Linux 常见流程是:
- 内核发现设备并在内部设备模型中注册。
- devtmpfs 提供基本设备节点。
- 内核发送 uevent。
- udev 或 systemd-udevd 根据规则创建设备节点、设置权限、建立符号链接和触发辅助动作。
- 用户通过
/dev访问设备。
常见稳定路径包括:
ls -l /dev/disk/by-uuid/
ls -l /dev/disk/by-id/
ls -l /dev/mapper/
/dev/sda 这类名称可能随设备枚举顺序变化;UUID、文件系统 LABEL 或稳定的 by-id 路径通常更适合写入 /etc/fstab。不过 by-id 的稳定性取决于硬件和虚拟化环境,虚拟机中可能包含变化的序列号。
查看设备关系:
lsblk -o NAME,TYPE,SIZE,FSTYPE,LABEL,UUID,MOUNTPOINTS
blkid
lsblk 展示块设备拓扑;blkid 读取文件系统或分区识别信息。后者在设备异常、权限不足或数据损坏时可能无法返回完整结果。
7.5 /dev 中的危险操作
以下命令会破坏目标设备上的数据:
sudo dd if=image.iso of=/dev/sdX bs=4M status=progress conv=fsync
这里的 /dev/sdX 必须确认是目标设备,而不是系统盘。/dev/sdX1 和 /dev/sdX 的含义不同:
- 写入
/dev/sdX1:通常只覆盖某个分区范围。 - 写入
/dev/sdX:可能覆盖分区表以及整个设备上的所有内容。
执行前应至少检查:
lsblk -o NAME,SIZE,MODEL,SERIAL,MOUNTPOINTS
findmnt
写入设备前卸载相关文件系统,避免文件系统仍在使用。dd 成功返回只表示写系统调用成功完成,不表示镜像内容正确、设备可启动或目标选择正确;写入后应通过校验和、重新读取分区表和挂载测试验证。
八、挂载、设备和文件系统之间的完整关系
以一个独立数据分区为例:
物理磁盘
↓
分区 /dev/nvme0n1p2
↓
文件系统 ext4
↓
挂载到 /var/lib/app
↓
应用访问 /var/lib/app/data.db
各层职责不同:
- 磁盘提供持久化存储介质。
- 分区划分设备地址范围。
- 文件系统组织 inode、目录、数据块和日志。
- 设备节点提供内核设备访问入口。
- 挂载把文件系统根目录接入 VFS 目录树。
- 应用路径经过 VFS 解析到具体 inode。
如果 /dev/nvme0n1p2 存在但不能挂载,可能是:
- 分区没有文件系统。
- 文件系统类型识别错误。
- 文件系统损坏。
- 设备仍被其他映射层占用。
- 加密层尚未解锁。
- 权限、挂载选项或安全策略阻止操作。
诊断通常按层进行:
lsblk -f
blkid /dev/nvme0n1p2
findmnt /var/lib/app
dmesg --level=err,warn | tail -n 50
不要在未确认文件系统类型时直接运行 fsck,也不要对已挂载且正在使用的文件系统执行修复操作。不同文件系统对应不同检查工具,例如 ext4 常用 e2fsck,XFS 使用 xfs_repair;具体工具和离线要求必须以文件系统文档为准。
九、挂载点与权限:路径可见不等于可以访问
文件访问至少受到以下因素影响:
- 路径中每级目录的执行权限(
x)。 - 目标文件的读写权限。
- 所属用户和组。
- ACL。
- SELinux、AppArmor 等强制访问控制。
- mount namespace。
- 挂载选项,如
ro、nosuid、nodev、noexec。 - 远程文件系统的身份映射和网络状态。
例如,nodev 并不会阻止目录中出现普通文件,而是禁止把其中的设备节点当作设备使用;noexec 主要限制从该挂载点直接执行程序,并不等同于阻止所有解释器读取脚本;nosuid 会限制 set-user-ID、set-group-ID 和相关能力效果。具体行为还受内核和文件系统实现影响。
检查选项:
findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /tmp
只看目录权限:
namei -l /var/lib/app/data.db
namei 会逐级显示路径,有助于定位“文件权限看起来正确,但仍然 Permission denied”的问题。因为父目录缺少执行权限时,即使目标文件本身是 644,也无法访问。
十、常见误解与失败路径
10.1 “/proc 是磁盘上的目录”
错误。/proc 通常是 proc 文件系统挂载点。执行:
df -T /proc
会看到文件系统类型类似 proc,而不是 ext4 或 XFS。重启后其中的进程目录、内存统计和内核状态会重新生成。
10.2 “删除挂载点目录就能卸载”
错误。目录只是挂载目标,删除它不会替代 umount。挂载状态由 namespace 中的挂载对象维护:
mount | grep ' /data '
findmnt /data
先卸载,再删除不再需要的空目录。
10.3 “du 和 df 不一致一定是统计错误”
不一定。常见原因是:
- 进程仍打开一个已删除文件,
df仍计入空间,du找不到目录项。 - 某个目录被另一个文件系统挂载,
du的遍历范围与磁盘统计不同。 - 文件系统保留块、元数据、日志或快照占用空间。
- 稀疏文件的逻辑大小和实际块占用不同。
已删除但仍打开的文件可检查:
sudo lsof +L1
找到进程后,通常应通过应用自身的日志轮转或重启流程释放文件描述符;直接删除 /proc/<pid>/fd/<n> 并不是通用修复方法。
10.4 “看到 /dev/sdb 就代表它已经挂载”
错误。设备节点存在只说明内核和用户空间提供了设备访问入口。检查挂载状态:
lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINTS
findmnt
设备可以存在但未分区、未格式化、未解锁或未挂载。
10.5 “sysfs 属性就是普通配置文件”
错误。读取通常会进入内核回调,写入也可能触发真实硬件或内核状态变化。错误写入可能导致设备下线、性能改变、链路重置或服务中断。应优先使用设备管理工具、网络工具、systemd 工具或发行版配置机制,而不是直接修改 sysfs。
10.6 “卸载成功就代表应用已经停止使用数据”
卸载针对的是文件系统在当前 namespace 中的挂载关系。进程可能仍持有:
- 打开的文件描述符。
- 当前工作目录。
- mmap 映射。
- 对下层设备的直接引用。
- 其他 namespace 中的独立挂载。
因此存储维护前应同时检查挂载点、进程、服务状态和映射层,而不能只看 umount 的返回值。
十一、一个可重复的诊断流程
当“某目录空间异常、设备找不到或挂载失败”时,可以按以下顺序缩小范围:
第一步:确认路径当前属于哪个文件系统
findmnt -T /var/lib/app
df -hT /var/lib/app
-T 根据路径查找当前挂载点,避免把父文件系统误当成目标文件系统。
第二步:确认块设备拓扑
lsblk -o NAME,TYPE,SIZE,FSTYPE,UUID,MOUNTPOINTS
观察是否经过:
磁盘 → 分区 → md RAID → LVM → 加密映射 → 文件系统
不要根据 /dev/sdX 的字母单独判断设备身份。
第三步:确认挂载和 namespace
findmnt -R /var/lib/app
cat /proc/self/mountinfo | grep '/var/lib/app'
如果程序运行在容器、systemd 服务或其他隔离环境中,还要确认它所在的 mount namespace。
第四步:确认进程是否仍在使用
sudo fuser -vm /var/lib/app
sudo lsof +D /var/lib/app
若目录包含大量文件,优先使用 fuser 或针对性查询,避免无边界遍历。
第五步:查看内核错误
dmesg --level=err,warn | tail -n 100
journalctl -k -p warning..alert --no-pager
I/O error、超时、文件系统只读重挂载、设备重置等线索通常会出现在内核日志中。
第六步:恢复时按层操作
- 设备不存在:检查硬件、虚拟设备、控制器和内核日志。
- 映射层不存在:检查加密、LVM、RAID 或 multipath 状态。
- 文件系统损坏:安排离线检查和备份恢复。
- 挂载点被占用:停止服务、离开工作目录、释放文件描述符。
/etc/fstab错误:修正配置后使用mount -av验证。- 空间不足:分别检查块空间、inode、已删除打开文件、日志、快照和保留空间。
修复动作必须先确定对象。尤其不能把“挂载失败”直接等同于“重新格式化”;mkfs 会初始化文件系统元数据,通常会破坏目标设备上原有数据。
十二、需要稳定记住的边界
FHS 解决的是目录职责,不决定底层数据一定在哪个分区;挂载解决的是文件系统如何接入路径树,不改变文件系统内部的 inode 和数据组织;proc 和 sysfs 提供内核状态的文件化接口,不是普通持久化目录;/dev 中的设备节点提供驱动访问入口,不是设备内容本身。
一个完整的判断可以写成:
路径含义
= FHS 约定
+ 当前 mount namespace 中的挂载关系
+ 对应文件系统类型
+ 文件系统和设备层的状态
+ 权限与安全策略
只有沿着这条链路逐层确认,才能解释诸如“文件为什么突然消失”“目录为什么占用空间不一致”“设备存在但无法挂载”“容器里为什么看不到宿主机目录”这类实际问题。
系列导航与关联阅读
- 系列入口:Linux 完整学习路线:从内核与文件系统到网络、性能和生产运维
- 上一篇:Linux 启动完整流程:Firmware、Bootloader、Kernel、initramfs 与 systemd
- 下一篇:Linux 文件、Inode 与链接:描述符、硬链接、符号链接和删除语义
- 延伸:Linux 块设备与存储:分区、LVM、RAID、挂载和在线扩容
官方资料
本文依据 Linux 内核、systemd 与主流发行版官方文档重新梳理;正文与实验由 WR BLOG 编写。

评论
0 条讨论