Linux 系统裁剪

系统裁剪简介

固件中通常包含 boot0、 uboot、 kernel、 rootfs 等镜像。基于经验,各个镜像尺寸的量级如下表所示:

Image size
boot0 < 100k
uboot < 1M
kernel 3-15M
rootfs > 4M

可以看到 boot0、 uboot、 kernel、 rootfs 的尺寸是依次增大的。对于大尺寸的裁剪效果往往比小尺寸的裁剪效果明显,比如 rootfs 裁剪 1M 可能很容易,对于 uboot 来说,则非常困难。因此,后续主要介绍 kernel 以及 rootfs 的裁剪。

boot0 裁剪

由于 boot0 很小,通常来说 boot0 代码也不开源,因此略过。

uboot 裁剪

uboot 代码位于 sdk/vendor/brandy/u-boot 目录下,主要有下面两种裁剪思路:

  • 修改 uboot 配置文件,删减不需要的配置。 uboot 配置文件通常位于源码下 include/configs/${CHIP}.h 或者 configs/${CHIP}_*_defconfig
  • 删除不需要的 uboot 命令

内核裁剪

通常关于 Linux 内核裁剪主要有如下方法:

  • 删除不使用的功能。如符号表、打印、调试等功能
  • 删除不使用的驱动
  • 修改内核源代码
  • 内核压缩

删除不使用的功能

根据自己的功能需求进行开启或关闭对应模块功能。

删除不使用的驱动

方案明确之后,所需的内核驱动也明确了。可以执行 make kernel_menuconfig,将没有用到的驱动关闭。

修改内核源代码

内核源码庞大,直接修改往往难度很大,可借助相关工具来评估模块以及符号的大小,然后进行针对性的裁剪。

  • size 工具

size 命令可查看内核镜像的 text、 data、 bss 等段的大小。如执行"size vmlinux",将会得到:

1
2
text data bss dec hex filename
5818117 1378944 168972 7366033 706591 vmlinux
  • nm 命令

nm 命令可查看内核模块中各个符号的尺寸。如执行"nm --size -r vmlinux | head -10",可得到:

1
2
3
4
5
6
7
8
9
10
000b0000 D pwrsv_lgc_tab
00004ad8 T hidinput_connect
00004000 b __log_buf
00003fb4 T __blockdev_direct_IO
00002ba4 t machines
000026a0 b g_fbi
00002120 t test_atomics
00002000 b page_address_maps
00002000 d nmi_print_seq
00002000 D init_thread_union

说明,一共有三列数据,分别表示大小、符号类型、符号名。其中符号类型:

  • b/B - 符号位于 bss 段
  • t/T - 符号位于 text 段
  • d/D - 符号位于 data 段

如果某些函数或者全局变量占用较大,可以进行针对性的优化。

文件系统裁剪

对于文件系统裁剪来说,主要思路是删、换、压。

  • 删,删除不需要的内容。如帮助文档、没用到的库、调试程序等
  • 换,使用小尺寸的实现替换大尺寸的实现。如使用 musl libc 库替换 glibc 库等
  • 压,使用合适的压缩算法

应用程序及冗余文件裁剪

在不影响整体功能的情况下,一些应用程序或冗余文件往往可以删除:

  • 调试工具。比如 tcpdump、 mpstat、 strace 等等
  • 性能测试工具。比如 lmbench、 sysstat、 tiobench 等等
  • 冗余文件。帮助文档、辅助程序、配置文件和数据模块等,又比如很多应用有相同的共能,只留其一
  • 采用具有通用功能的替代软件包。 Linux 上有许多具有相似功能的软件包,可以选择其中占存储空间较小的软件包并移植到嵌入式设备上
  • 资源文件。一些音视频以及 UI 资源往往占用很大空间,如果没有用到,也需要删除

库的裁剪

关于库的裁剪主要有两个思路:

  • 使用较小的 C 库,如 musl libc, uclibc 等来替换 glibc
  • 删除没有用到的库

删除没用到的库

嵌入式产品通常应用程序有限,因此可能存在很多库不会被用到,可以进行删除。当前 Tina 环境提供了一种删除方法,执行 make menuconfig,打开如下选项

1
2
3
Tina Configuration
Target Images --->
[*] downsize the root filesystem or initramfs

打开之后,在生成 rootfs/initramfs 之前会对其中没有用到的库进行删除。

具体可参考 scripts/reduce-rootfs-size.sh 文件,其主要思路是:

  • 分析 rootfs 下的应用程序所依赖的库
  • 分析 “应用程序依赖库” 所依赖的库,一直递归下去,直到完全找出所有依赖的库
  • 根据上述查找结果,删除没有被依赖的库

应用程序与库 strip

strip 会去掉应用程序与库的符号信息和调试信息,大大减少空间占用。

当前 Tina 环境下默认开启了 strip 功能,如果没开启,请确保开启以减少空间占用。

1
2
3
Tina Configuration
Global build settings --->
Binary stripping method (strip) --->

文件系统压缩

有些文件系统支持压缩,有些不支持。比较常用的是 squahfs、 ext4、 jfss2 三种文件系统。具体可执行 make menuconfig 进行选择:

1
2
3
4
5
6
Tina Configuration
Target Images --->
*** Root filesystem images ***
[ ] ext4 ----
[ ] jffs2
[*] squashfs --->

常见的压缩有 lzop, gzip, xz 等,压缩率最高的是 xz。但是 xz 压缩解压最慢,非常影响启动速度。实际在选择压缩方式时应综合考虑

参考文献

[1] https://elinux.org/Kernel_Size_Tuning_Guide
[2] Karim Yaghmour. Building Embedded Linux Systems [M]
[3] Michael Opdenacker. Embedded Linux size reduction techniques
[4] https://tiny.wiki.kernel.org
《全志SDK文档》