书籍:嵌入式 Linux 知识库 原文:eLinux.org 翻译:@wengpingbo 校订:@lzufalcon
本文介绍一些与 Linux 系统尺寸优化相关的信息和项目。
另一个 WIKI,https://tiny.wiki.kernel.org/,在内核尺寸优化方面,有一些新的信息和工作(截止到 2014.08)。
Andi Kleen 在 2014.05,提交了一系列补丁,用来减少 Linux 内核网络协议栈的大小。相关的提交记录在这里: https://lkml.org/lkml/2014/5/5/686
Andi 指出这些补丁支持 3 个使用场景:
为了获得全面的尺寸缩减,在使用这些补丁的时候,最好同时使用 LTO。这样,网络协议栈只需 170K 就能跑起来(默认的协议栈需要 400K)
这里有一篇 LWN 文章讨论了用于裁剪内核的 3 个 GCC 选项。
第一个选项是 -Os
,这早就在 tiny kernel 补丁里。
在 3.4 版本之后,GCC 提供一个 -funit-at-a-time
选项。这让 GCC 能够更好的移除内联和无用的代码,减少 text 和 data 段的大小。这个选项依赖另一个补丁。根据 GCC 手册,这个选项不再起作用。
-fwhole-program
和 --combine
选项的组合,其效果上等于把所有的源文件分组,且把所有的变量静态化。GCC 依旧支持这些选项,但在 BusyBox 的配置选项里已经不提供了。这发生了什么?
另外一个选项,-mregparm=3
,看上去是 X86 特有的,它告诉编译器对于函数的前 3 个参数,用寄存器存储。(by John Rigby)
访问 1(http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html) 查阅所有可用的优化选项,访问 编译器优化 查阅更多关于优化选项效果的细节。
这些 补丁 ,通过在链接阶段提高无用代码 / 数据的剔除效果,可以缩减大约 10% 的内核大小。它们正在往内核主线提交。由于一个链接器的 BUG,这些补丁的接受依赖于一个新版本链接器(在 binutils-2.21 将会提供)。好消息是这个 BUG 只是影响一些特定的架构(parisc),所以这些补丁在旧的链接器上还是可以使用的。
通常,内核内存大小的缩减主要关注内核静态编译的镜像大小。但是,内核在运行时也要动态分配内存。在加载时,内核会创建多个表,给网络,文件系统等模块使用。
以下表格展示了,在 2.6 版本内核里,不同的哈希表所占用的内存大小。(表格来自 data_structures 第 25 页)
Hash Table | memory < 512MiB RAM | memory >=512MiB RAM |
---|---|---|
32b/64b | 32b/64b | |
TCP established | 96k/192k | 384k/768k |
TCP bind | 64k/128k | 256k/512k |
IP route cache | 128k/256k | 512k/1M |
Inode-cache | 64k/128k | 64k/128k |
Dentry cache | 32k/64k | 32k/64k |
Total | 384k/768k | 1248k/2496k |
内核有一个配置选项,用来减少每一个进程的内核栈大小到 4K。内核栈大小默认是 8K(截止到 2011)。如果你有很多进程,使用 4K 的栈能够减少内核栈的使用。
更多关于内核栈大小的信息: 小型内核栈
在 2012 年,Tim Bird 研究了几种关于自动尺寸缩减和整体系统优化的技术。特别是他研究的如下几项:
Tim 同时也在链接时重写和静态代码压缩技术方面发现了一些非常有趣的学术研究。(注:部分研究见 Tiny Linux Kernel)Tim 在 2013 年 5 月在日本举行的 LinuxCon 会议上展示了他的工作。
这次展示的一些提纲和完整的幻灯片可以这里找到 系统尺寸自动缩减
在 2014 年,一个开放项目提议,压缩的 printk 消息,评估过这个技术。这个项目的结果可以在 压缩的 printk 消息 - 结果 找到。
一群开发者正持续致力于 Linux 内核大小裁剪的工作上(截止到 2014)。为方便后续内核裁剪工作,已经建立了一篇文章来分类近期工作和想法。该文章在:内核大小裁剪工作。
对于只读数据来说,一个压缩的文件系统是很有的。以下文件系统在嵌入式系统里用得非常多:
要注意的是由于 Cramfs 和 Squashfs 只写一次(write-only-once)的天然特性,也能够用于 MTD 存储。
访问 文件系统 获取更多信息。
你可以使用 gcc -Os
来优化大小。
你可以使用 'strip' 命令来剔除你应用中无用的符号。strip
命令包含在你的工具链里,且和当前的架构有关。(例如,你需要加上一个工具链前缀,像 arm-linux-strip
)。
要注意的是这会让你的应用更加难调试,因为调试符号不再存在。
默认情况下,strip
只移除调试符号。但是在动态链接时,我们可以移除除了基本符号之外的所有其他符号。若想获取最高程度的移除效果,可以使用 strip --strip-unneeded <app>
。
这可以节省很多空间,特别是在调试符号被包含在构建里时。
$ gcc -g hello.c -o hello
$ ls -l hello
-rwxrwxr-x 1 tbird tbird 6143 2009-02-10 09:43 hello
$ strip hello
$ ls -l hello
-rwxrwxr-x 1 tbird tbird 3228 2009-02-10 09:43 hello
$ strip --strip-unneeded hello
$ ls -l hello
-rwxrwxr-x 1 tbird tbird 3228 2009-02-10 09:43 hello
现在,编译时不带调试符号:
$ gcc hello.c -o hello
$ ls -l hello
-rwxrwxr-x 1 tbird tbird 4903 2009-02-10 09:45 hello
$ strip hello
$ ls -l hello
-rwxrwxr-x 1 tbird tbird 3228 2009-02-10 09:45 hello
我们可以 strip 可执行文件和共享库。
有一个 "super-strip" 工具,会移除 ELF 可执行程序里额外信息(通常 'strip' 忽略的)。可以在这里找到:http://muppetlabs.com/~breadbox/software/elfkickers.html。这个程序现在好像被废弃了,我不能在 Fedora 8 上编译它。
这里 有一些关于如何手动剔除单独节区(Sections)的信息,它介绍了如何使用 -R
命令。
如果你非常想要创建小尺寸二进制文件,你可以使用一些技术来手动创建最小的 Linux 可执行文件。
看 一个快速创建紧凑的 Linux ELF 可执行文件的教程
Glibc 是 Linux 系统下默认的 C 库。Glibc 大概有 2MB 大小。Linux 上同样可以找到其他 C 库,他们提供不同程度上的兼容和尺寸节约。通常,对于那些比较关注系统尺寸来说,uClibc 是一个非常好的 Glibc 替代品。
如果你的应用非常小,那么使用静态链接比使用共享库更合理一些。共享库默认包含所有特性的符号(函数和数据结构)。但是,当你把一个库静态链接到一个程序时,只有那些实际被引用的部分才会被包含到程序里来。
通过剔除无用的符号,减少共享库的大小是可能的。
MontaVista 释放过一个工具,用来优化库。这个工具扫描整个文件系统,然后会重新构建系统上的共享库,只包含真正被当前文件系统上的应用引用过的符号。
使用这个方法需要当心,因为这会让那些程序的插件,或者部分升级过程变得非常困难(因为新软件引用的符号可能没有包含在当前已经被优化过的库里)。但是对于那些功能固定的设备来说,这会显著的减少库的大小。
看 http://libraryopt.sourceforge.net/
对于一个产品,通过延迟加载共享库,和分割库的依赖,来减少运行时 RAM 的占用空间是可能的。Panasonic 做了一些研究,在一个进程里延迟库的加载,在 ELC 2007 展示过这个研究。
看 动态延时加载(pdf) 演示。
我们可以通过直接使用来自 Flash 的一些 text
和 data
来节省内存开销。
通过在 FLASH 里执行内核,这有可能节省内存。
通过在 FLASH 里执行应用程序,这有可能节省内存。
这有一个技术,用来在 FLASH 里保持数据,直到该数据需要更新,才复制到内存里来。
size vmlinux */built-in.o
[tbird@crest ebony]$ size vmlinux */built-in.o
text data bss dec hex filename
2921377 369712 132996 3424085 343f55 vmlinux
764472 35692 22768 822932 c8e94 drivers/built-in.o
918344 22364 36824 977532 eea7c fs/built-in.o
18260 1868 1604 21732 54e4 init/built-in.o
39960 864 224 41048 a058 ipc/built-in.o
257292 14656 34516 306464 4ad20 kernel/built-in.o
34728 156 2280 37164 912c lib/built-in.o
182312 2704 736 185752 2d598 mm/built-in.o
620864 20820 26676 668360 a32c8 net/built-in.o
1912 0 0 1912 778 security/built-in.o
133 0 0 133 85 usr/built-in.o
nm --size -r vmlinux
[tbird@crest ebony]$ nm --size -r vmlinux | head -10
00008000 b read_buffers
00004000 b __log_buf
00003100 B ide_hwifs
000024f8 T jffs2_garbage_collect_pass
00002418 T journal_commit_transaction
00002400 b futex_queues
000021a8 t jedec_probe_chip
00002000 b write_buf
00002000 D init_thread_union
00001e6c t tcp_ack
测量 Linux 下运行时内存使用情况,访问 动态内存测量
同时,要得到更精准的内存使用情况和相关补丁,请访问 精准内存测量
Linux 内核从 2.4 到 2.6 版本之间,大小增加了 10% ~ 30%。论坛成员非常关注这种尺寸增长。
访问 Szwg Linux 26Data 相关数据支持。
CSiBE 是 GCC 编译器的代码大小基准测试工具。CSiBE 的主要目标是监控 GCC 生成的代码大小。另外,编译时间和代码优化测量也包括在内。
"Motorola 在 2.4 的 Linux 内核上做的系统大小缩减(大概给移动设备使用)"
这有一些专注于小尺寸系统的项目:
meta-tiny 是我的一个实验项目,用来验证我们可以用现有的代码和架构构建什么东西。
我发现我们可以在不裁剪任何功能的前提下,节约大概 10% 的核心镜像空间。
我们可以在保持 IPV4 可用的前提下,节省 20% 空间。
ARM 公司的 Catalin Marinas 最近贡献了一个内存溢出检测工具给 Linux 内核(在 2.6.17 版本?)。将来可能会合入主线仓库。相关 LKML 讨论记录:http://lkml.org/lkml/2006/6/11/39
已经有理论证明减少系统大小可以提高性能,因为这减少了缓存匹配失败的几率。在 Linux 下好像没有实际数据来支持这个理论,但是在内核邮件列表里已经讨论过。
查看 this post by Linus Torvalds
这有一个很好的文档,是关于怎样从一个桌面发行版本里裁剪不需要的文件。实例的版本是 LFS,但是在其他发行版本上也是可以工作的。
http://www.linuxfromscratch.org/hints/downloads/files/OLD/stripped-down.txt
该节罗列了各种各样的,关于生成绝对小型系统的尝试。
分类: