第4章:根文件系统目录结构——FHS标准详解
说到根文件系统,很多人第一反应就是“一堆文件夹”。没错,但这一堆文件夹怎么摆、里面放什么,其实是有规矩的。这个规矩叫FHS,全称Filesystem Hierarchy Standard。说白了,它就是Linux世界里的“文件夹摆放公约”。
我刚开始做嵌入式的时候,觉得这玩意儿没啥用。反正我自己能认路就行。直到有一次接手一个项目,前任工程师把/bin和/sbin混着用,/usr/local里塞满了乱七八糟的东西。结果换了个编译器版本,整个系统就炸了。嗯,从那以后,我再也不敢小看FHS了。
4.1 FHS标准到底是什么?
FHS定义了Linux系统中各个目录的用途和存放内容。它不是一个强制标准,但几乎所有主流Linux发行版都遵循它。为什么?因为大家约定俗成,方便维护、方便移植。
你想想看,如果你写了一个脚本,里面用到/usr/bin下的工具。换到另一台机器上,如果那台机器的工具放在/opt/bin里,你的脚本就废了。FHS就是为了避免这种混乱。
核心原则:
- 静态与可变分离:静态文件(如可执行程序)和可变文件(如日志、缓存)分开存放
- 可共享与不可共享分离:可以在网络共享的目录(如/usr)和本地专用的目录(如/etc)分开
- 层次清晰:每个目录有明确的职责,不越界
下面这张图是我自己整理的嵌入式根文件系统核心目录结构,你可以把它当作“地图”来用。
4.2 核心目录详解
/bin —— 用户命令的“工具箱”
/bin存放的是最基本的用户命令。这些命令在单用户模式下也能用,说白了就是系统“救急”用的。比如ls、cp、mv、cat、echo这些。
我个人习惯把/bin里的东西控制在最小集。嵌入式设备存储空间有限,能省则省。我曾经见过有人把整个Busybox编译出来的200多个命令全塞进/bin,结果一个根文件系统占了50MB。其实很多命令根本用不上。
我的建议:嵌入式系统中,/bin只放那些系统启动和基本操作必须的命令。其他不常用的命令放到/usr/bin里。这样分区挂载时,/分区可以很小,/usr分区可以单独挂载。
/sbin —— 系统管理的“特权区”
/sbin存放的是系统管理命令。这些命令通常只有root用户才能用。比如mount、fdisk、mkfs、ifconfig等。
注意区分:/bin里的命令普通用户也能用,/sbin里的命令是给管理员用的。这个界限在FHS里写得很清楚。我在项目中遇到过有人把mount放到/bin里,结果普通用户也能挂载文件系统,这就有安全风险了。
/etc —— 系统的“配置文件仓库”
/etc是Linux系统里最重要的目录之一。它存放着系统的各种配置文件。比如:
passwd:用户账户信息fstab:文件系统挂载表inittab:init进程的配置文件init.d/:系统服务启动脚本network/:网络配置
嗯,这里要注意:/etc里的文件都是可变的。你改了配置,系统行为就会变。所以做嵌入式系统时,/etc通常放在可读写的文件系统上,比如JFFS2或UBIFS。
避坑指南:我曾经犯过一个错误——把/etc放在了只读的SquashFS里。结果系统启动后,想改个IP地址都改不了。后来只好重新制作文件系统。记住:/etc必须是可写的。
/lib —— 共享库的“大本营”
/lib存放的是系统运行必需的共享库和内核模块。比如libc.so、ld-linux.so这些。没有它们,系统连最基本的程序都跑不起来。
嵌入式系统里,/lib的大小往往是最头疼的。一个完整的glibc库动辄几MB,对于只有8MB Flash的设备来说,简直是灾难。所以我建议用musl libc或者uClibc来替代,能省下一大半空间。
另外,内核模块通常放在/lib/modules/$(uname -r)/下。这个目录在嵌入式系统里经常被忽略,结果驱动加载不了。我踩过这个坑,后来每次做根文件系统都会检查一下这个目录。
/usr —— 用户程序的“第二战场”
/usr是FHS里最复杂的目录。它本质上是一个“二级根文件系统”。里面也有bin、sbin、lib这些子目录。
为什么要搞这么复杂?因为历史原因。早期Unix系统把/放在小磁盘上,/usr放在大磁盘上。现在虽然硬件条件好了,但这个传统保留了下来。
在嵌入式系统里,我通常把/usr做成一个单独的分区。这样/分区可以很小(只放启动必需的东西),/usr分区可以放更多的应用程序。而且/usr可以做成只读的,提高系统稳定性。
/var —— 可变数据的“集散地”
/var存放的是系统运行过程中会变化的数据。比如:
log/:系统日志spool/:打印队列、邮件队列tmp/:临时文件(系统重启后保留)cache/:应用程序缓存
嵌入式系统里,/var通常放在RAMFS或者可读写的Flash分区上。因为日志和缓存会频繁写入,如果放在Flash上,要注意磨损均衡的问题。
关键点:FHS要求/var在系统启动时必须是空的。所以很多嵌入式系统在启动脚本里会清空/var的内容。这个细节容易被忽略,但很重要。
4.3 最小化目录创建
做嵌入式系统,存储空间就是命根子。能省则省。那么,一个能跑起来的根文件系统,最少需要哪些目录?
我根据多年经验,总结了一个“最小化目录清单”:
| 目录 | 是否必需 | 说明 |
|---|---|---|
| /bin | 必需 | 至少要有init、sh、mount等基本命令 |
| /sbin | 必需 | 至少要有init(如果放在这里) |
| /etc | 必需 | 至少要有inittab、fstab、passwd |
| /lib | 必需 | 至少要有libc.so、ld-linux.so |
| /dev | 必需 | 至少要有console、null、zero |
| /proc | 必需 | 内核虚拟文件系统,必须挂载 |
| /sys | 推荐 | 内核设备模型,很多驱动依赖它 |
| /tmp | 推荐 | 很多程序需要临时文件 |
| /var | 可选 | 如果没有日志需求,可以省略 |
| /usr | 可选 | 如果所有程序都放在/bin,可以省略 |
| /mnt | 可选 | 挂载点,按需创建 |
| /opt | 可选 | 第三方软件,按需创建 |
下面是一个创建最小化根文件系统的脚本示例:
#!/bin/bash
# 最小化根文件系统创建脚本
# 适用于嵌入式Linux系统
ROOTFS_DIR="./rootfs"
# 创建必需目录
mkdir -p ${ROOTFS_DIR}/{bin,sbin,etc,lib,dev,proc,sys,tmp,var/{log,run}}
# 设置权限
chmod 755 ${ROOTFS_DIR}/{bin,sbin,etc,lib,dev,proc,sys,tmp}
chmod 1777 ${ROOTFS_DIR}/tmp
# 创建基本设备节点(如果使用devtmpfs则不需要)
# mknod -m 600 ${ROOTFS_DIR}/dev/console c 5 1
# mknod -m 666 ${ROOTFS_DIR}/dev/null c 1 3
# mknod -m 666 ${ROOTFS_DIR}/dev/zero c 1 5
# 复制Busybox(假设已编译好)
cp /path/to/busybox ${ROOTFS_DIR}/bin/busybox
# 创建符号链接
cd ${ROOTFS_DIR}/bin
for cmd in sh ls cp mv cat echo mount umount; do
ln -sf busybox $cmd
done
# 复制必要的库文件
cp /path/to/libc.so ${ROOTFS_DIR}/lib/
cp /path/to/ld-linux.so ${ROOTFS_DIR}/lib/
echo "最小化根文件系统创建完成!"
echo "目录大小:"
du -sh ${ROOTFS_DIR}
经验之谈:这个脚本创建出来的根文件系统大概只有2-3MB。我曾在只有4MB Flash的ARM9开发板上跑过,完全没问题。当然,功能也有限,只能做基本的系统管理。但至少能启动到shell,让你有进一步操作的空间。
4.4 目录权限与所有权
目录创建好了,权限设置也不能马虎。FHS对目录权限有建议值:
| 目录 | 建议权限 | 所有者 | 说明 |
|---|---|---|---|
| /bin | 755 | root:root | 所有用户可读可执行 |
| /sbin | 755 | root:root | 所有用户可读,但只有root可执行 |
| /etc | 755 | root:root | 所有用户可读,只有root可写 |
| /lib | 755 | root:root | 所有用户可读可执行 |
| /tmp | 1777 | root:root | 粘滞位,所有用户可读写 |
| /var/log | 755 | root:root | 只有root可写 |
嗯,这里有个细节:/tmp的权限是1777。这个1就是粘滞位(sticky bit)。它的作用是:虽然所有用户都能在/tmp里创建文件,但只能删除自己的文件。防止A用户删了B用户的临时文件。
我曾经踩过的坑:有次做项目,忘了给/tmp设置粘滞位。结果一个程序运行时产生的临时文件被另一个程序误删了,导致整个系统崩溃。排查了半天才发现是权限问题。从那以后,我每次创建目录都会检查权限设置。
4.5 嵌入式系统的特殊考虑
嵌入式系统和桌面Linux不一样,存储资源非常有限。所以FHS在嵌入式领域有一些“变通”做法:
- 合并目录:有些嵌入式系统把
/bin和/sbin合并,所有命令都放在/bin里。虽然不符合FHS,但能省一个目录项。 - 使用符号链接:比如
/bin->/usr/bin,减少根目录下的目录数量。 - 只读根文件系统:把
/做成只读的SquashFS,/etc和/var放在可读写的分区上。 - 使用Busybox:一个Busybox二进制文件就能提供200多个命令,大大节省空间。
我个人习惯的做法是:先按照FHS标准创建完整的目录结构,然后根据实际需求裁剪。这样既保证了兼容性,又控制了大小。毕竟,标准是死的,人是活的。但前提是,你得先懂标准,才知道怎么变通。
好了,关于根文件系统目录结构,就讲到这里。记住一句话:目录结构是骨架,文件内容是血肉。骨架搭好了,后面的事情就顺了。
本章核心要点:
- FHS是Linux目录结构的“公约”,嵌入式系统也要尽量遵守
- /bin放用户命令,/sbin放系统管理命令,/etc放配置,/lib放库
- /usr是二级根文件系统,/var放可变数据
- 最小化根文件系统只需要/bin、/sbin、/etc、/lib、/dev、/proc这6个目录
- 目录权限和所有权设置不能马虎,特别是/tmp的粘滞位
- 嵌入式系统可以灵活变通,但前提是理解标准
可可精品资料库