4. 内核裁剪实战-文件系统:VFS层裁剪、具体文件系统(ext4、squashfs、ubifs)的配置选项

文件系统这块,说实话是嵌入式Linux裁剪里最容易被低估的环节。很多人觉得文件系统嘛,能读写就行。但我在实际项目中吃过不少亏——有次因为VFS层没裁干净,一个只读设备愣是多占了200KB的内核空间。200KB在服务器上不算什么,但在只有8MB Flash的工业控制器上,那就是生死线。

所以这一章,咱们就聚焦在文件系统的裁剪上。我会从VFS层讲起,再到具体的ext4、squashfs、ubifs,把每个关键配置选项掰开揉碎了说清楚。

4.1 VFS层:文件系统的骨架,能裁多少?

VFS(Virtual File System)是Linux文件系统的抽象层。说白了,它就是个调度中心——你调用open()、read()、write(),VFS负责把这些请求路由到具体的文件系统驱动上。

VFS层能不能裁?能,但得小心。我个人的习惯是:保留核心调度功能,砍掉不用的辅助特性。

4.1.1 核心配置选项

在make menuconfig中,VFS相关的选项主要集中在:

File systems --->
    [*] Virtual file system support (CONFIG_VFS)
    [ ]   VFS POSIX ACL support (CONFIG_POSIX_ACL)
    [ ]   VFS security labels (CONFIG_SECURITY)
    [ ]   VFS filesystem notifications (CONFIG_FSNOTIFY)
    [ ]   VFS inotify support (CONFIG_INOTIFY_USER)
    [ ]   VFS dnotify support (CONFIG_DNOTIFY)
    [ ]   VFS fanotify support (CONFIG_FANOTIFY)
注意:CONFIG_VFS是必选的,几乎所有文件系统都依赖它。但下面的子选项,大部分工业场景都可以关掉。

我列个表,方便你对照着裁:

配置项 功能说明 工业场景建议 节省空间(约)
CONFIG_POSIX_ACL 访问控制列表,多用户权限管理 单用户设备可关 8-12KB
CONFIG_SECURITY 安全标签,如SELinux 非安全关键系统可关 15-25KB
CONFIG_INOTIFY_USER 文件变更通知(用户态) 无监控需求可关 10-15KB
CONFIG_DNOTIFY 目录变更通知(旧版) 已废弃,建议关 5-8KB
CONFIG_FANOTIFY 文件系统事件通知(新版) 无审计需求可关 12-18KB

我曾经在一个工业数据采集器项目里,把上面这些子选项全关了,VFS层从原来的280KB降到了220KB左右。虽然绝对值不大,但在Flash寸土寸金的场景下,这60KB可能就是压死骆驼的最后一根稻草。

4.2 ext4:最通用的选择,但别全要

ext4是Linux桌面和服务器上的标配。但在嵌入式里,它有点「重」。不过如果你需要大容量存储、日志恢复、动态扩展,ext4仍然是首选。

4.2.1 ext4的裁剪要点

进入ext4配置路径:

File systems --->
    <*> The Extended 4 (ext4) filesystem (CONFIG_EXT4_FS)
    [ ]   Ext4 POSIX Access Control Lists (CONFIG_EXT4_FS_POSIX_ACL)
    [ ]   Ext4 Security Labels (CONFIG_EXT4_FS_SECURITY)
    [ ]   Ext4 Encryption (CONFIG_EXT4_ENCRYPTION)
    [ ]   Ext4 Journal (CONFIG_EXT4_FS_JOURNAL)
    [ ]   Ext4 Extended Attributes (CONFIG_EXT4_FS_XATTR)
    [ ]   Ext4 Multi-block allocator (CONFIG_EXT4_MB)
核心建议:工业场景下,如果不需要加密和复杂权限,可以关掉ACL、Security、Encryption。但Journal建议保留——掉电保护就靠它了。

我遇到过最极端的情况:一个PLC项目,要求ext4只读挂载。我把Journal也关了,因为只读不需要日志。结果省了约40KB。但如果你要读写,Journal千万别动,否则掉电必丢数据。

4.2.2 挂载参数优化

除了内核配置,挂载参数也能省内存:

# 只读挂载,省去日志和元数据缓存
mount -t ext4 -o ro,noatime,nodelalloc /dev/mmcblk0p1 /mnt

# 读写挂载,但减少内存占用
mount -t ext4 -o rw,noatime,nobarrier,data=writeback /dev/mmcblk0p1 /mnt

noatime可以关掉访问时间更新,减少写操作。nobarrier在电池供电设备上可以开,但要注意掉电风险。

4.3 squashfs:只读文件系统的王者

squashfs是我在嵌入式项目里用得最多的文件系统。它只读、高压缩、加载快,特别适合存放根文件系统或固件镜像。

4.3.1 压缩算法选择

squashfs支持多种压缩算法,配置路径:

File systems --->
    Miscellaneous filesystems --->
        <*> SquashFS 4.0 - Squashed file system support (CONFIG_SQUASHFS)
        [*]   SquashFS XZ compression (CONFIG_SQUASHFS_XZ)
        [*]   SquashFS LZO compression (CONFIG_SQUASHFS_LZO)
        [*]   SquashFS LZ4 compression (CONFIG_SQUASHFS_LZ4)
        [*]   SquashFS ZSTD compression (CONFIG_SQUASHFS_ZSTD)

选哪个?我直接给结论:

算法 压缩比 解压速度 适用场景
gzip(默认) 中等 快 通用场景
XZ 最高 慢 Flash极小,启动时间不敏感
LZO 低 极快 需要快速解压,如实时系统
LZ4 较低 极快 与LZO类似,但更稳定
ZSTD 高 快 平衡场景,我推荐这个

我个人习惯用ZSTD。压缩比接近XZ,但解压速度快得多。在一个工业相机项目里,我用ZSTD把16MB的根文件系统压到了5.2MB,启动时间只增加了0.3秒。

4.3.2 裁剪建议

squashfs本身很轻量,但要注意:

  • 只保留需要的压缩算法:如果你只用ZSTD,就把其他算法全关了。每个算法大约增加10-15KB内核大小。
  • 关掉调试选项:CONFIG_SQUASHFS_DECOMP_MULTI多线程解压,单核CPU可以关。
  • 关掉碎片整理:CONFIG_SQUASHFS_FRAGMENT_CACHE,只读系统不需要。

4.4 ubifs:NAND Flash的最佳搭档

ubifs是专门为NAND Flash设计的文件系统。它基于UBI层,能处理坏块管理、磨损均衡。如果你用NAND Flash,ubifs几乎是唯一的选择。

4.4.1 配置路径

File systems --->
    Miscellaneous filesystems --->
        <*> UBIFS file system support (CONFIG_UBIFS_FS)
        [ ]   UBIFS Encryption (CONFIG_UBIFS_FS_ENCRYPTION)
        [ ]   UBIFS Authentication (CONFIG_UBIFS_FS_AUTH)
        [ ]   UBIFS Extended attributes (CONFIG_UBIFS_FS_XATTR)
        [ ]   UBIFS LZO compression (CONFIG_UBIFS_FS_LZO)
        [ ]   UBIFS ZSTD compression (CONFIG_UBIFS_FS_ZSTD)
经验之谈:ubifs的压缩算法建议保留LZO或ZSTD中的一个。NAND Flash写入速度慢,压缩可以减少写入量,延长Flash寿命。

4.4.2 关键参数调整

ubifs的性能和空间利用率,很大程度上取决于挂载参数:

# 挂载ubifs,指定LEB大小和压缩
mount -t ubifs -o compr=lzo,noatime /dev/ubi0_0 /mnt

# 如果Flash空间紧张,可以关掉压缩
mount -t ubifs -o compr=none,noatime /dev/ubi0_0 /mnt

这里有个坑:LEB(Logical Erase Block)大小必须与UBI层对齐。我曾经在一个项目里,UBI层配置的LEB大小是128KB,但ubifs默认用了256KB,结果挂载失败。排查了半天才发现是参数不匹配。

4.4.3 裁剪对比

我把三种文件系统的裁剪前后对比列出来,方便你决策:

文件系统 默认内核大小 裁剪后大小 节省空间 适用场景
ext4 ~350KB ~250KB 100KB 大容量存储、读写频繁
squashfs ~80KB ~50KB 30KB 只读根文件系统、固件
ubifs ~120KB ~80KB 40KB NAND Flash、掉电敏感

4.5 知识结构图:文件系统裁剪全景

下面这张图,我把VFS层和三种文件系统的裁剪关系画出来了。你可以把它当作裁剪时的路线图:

文件系统裁剪知识结构图 文件系统裁剪全景图 VFS 核心层 POSIX ACL Security Labels inotify/fanotify dnotify(可关) ext4 squashfs ubifs 裁剪要点总结 • ext4:关ACL、Security、Encryption;保留Journal(读写场景) • squashfs:只保留一种压缩算法(推荐ZSTD);关多线程解压 • ubifs:保留一种压缩算法(LZO或ZSTD);关Encryption和Authentication • VFS层:关inotify/fanotify/dnotify;单用户设备关POSIX ACL

4.6 实战:一个典型的裁剪案例

最后,我分享一个实际项目的裁剪配置。这是一个工业数据记录仪,使用NAND Flash + ubifs存储日志,squashfs存放只读根文件系统。

内核配置片段:

# VFS层
CONFIG_VFS=y
# CONFIG_POSIX_ACL is not set
# CONFIG_SECURITY is not set
# CONFIG_INOTIFY_USER is not set
# CONFIG_DNOTIFY is not set
# CONFIG_FANOTIFY is not set

# ext4(用于外部SD卡)
CONFIG_EXT4_FS=y
# CONFIG_EXT4_FS_POSIX_ACL is not set
# CONFIG_EXT4_FS_SECURITY is not set
# CONFIG_EXT4_ENCRYPTION is not set
CONFIG_EXT4_FS_JOURNAL=y

# squashfs(根文件系统)
CONFIG_SQUASHFS=y
CONFIG_SQUASHFS_ZSTD=y
# CONFIG_SQUASHFS_XZ is not set
# CONFIG_SQUASHFS_LZO is not set
# CONFIG_SQUASHFS_LZ4 is not set
# CONFIG_SQUASHFS_DECOMP_MULTI is not set

# ubifs(日志存储)
CONFIG_UBIFS_FS=y
CONFIG_UBIFS_FS_LZO=y
# CONFIG_UBIFS_FS_ENCRYPTION is not set
# CONFIG_UBIFS_FS_AUTH is not set
# CONFIG_UBIFS_FS_XATTR is not set

这个配置最终内核大小约1.8MB,比默认配置小了约600KB。设备运行两年,没出过文件系统相关的问题。

我的建议:裁剪文件系统时,先确定你的存储介质和读写模式。NAND Flash用ubifs,NOR Flash用squashfs或jffs2,SD卡/eMMC用ext4。别混用,也别贪多。一个设备里同时支持三种文件系统的情况很少见,除非你有特殊需求。