嵌入式Linux启动时间优化的秘密之一工具链/应用程序优化
嵌入式Linux启动时间优化的秘密
01
工具链/应用程序优化
导读:嵌入式Linux在应用中往往希望系统能在尽量短的时间内启动,以提高用户体验。而且在有的应用场合,对启动时间具有严格的时间要求,尤其在工业或者医疗器械应用领域。此时如何加快Linux的启动,将成为一个挑战,对于大多数应用开发人员而言,由于Linux系统的复杂性,对于如何提高启动速度,往往无从下手。那么阅读完本文,将获得清晰完整的解决思路。
1.降低启动时间的一般思路
在准备降低系统的启动时间时,思路上应建立以下的切入点:
最快的代码是未执行的代码。
引导操作本质上的很大一部分工作实际上是将代码和数据从存储设备加载到RAM。如所需加载内容越少则意味着加载操作越快。
如果根文件系统越大,则安装时间可能会越长。
因此,即使未执行的代码也会延长启动时间。
另外在硬件方案设计时尽量选择读写速度快的存储介质。例如,从SD卡启动实际上比从NAND FLASH启动快。
2.启动时间测量方法
要降低系统的启动时间,则首先需要选择一个可靠的启动时间的测量方法:
在Linux代码中加入对某一个GPIO脚的逻辑电平控制,利用示波器测量GPIO状态。后面将介绍如何在代码中加入对GPIO的控制。
监控串口控制台报文以测量时间,可以使用grabserial。
参见https://elinux.org/Grabserial
3. 工具链优化
3.1 从工具链入手
选择使用合适的工具链,应是第一个入手点,因为所有的运行加载固件都是由工具链编译而成。如果尚未进行其他优化,则更改工具链的好处将更加明显,并且更容易度量。
您可以在工具链中进行以下更改,这可能会影响启动时间,性能和大小:
编译器版本:gcc和binutils的版本,最新版本往往可以具有更好的优化功能。
C库:glibc,uClibc,musl。使用uClibc和musl库编译的根文件系统更小
指令集变量:ARM或Thumb2,是否支持硬浮点。
可能会影响代码性能和代码大小(Thumb2编码与ARM相同的指令,但以更紧凑的方式,至少会显着减小大小)。
C库在创建工具链时进行了硬编码,可供选择的C库:
glibc:最标准且功能最全。http://www.gnu.org/software/libc/
uClibc:更小且可配置。已经存在约20年了。http://uclibc-ng.org/
musl:uClibc替代品,虽比较新但很成熟。http://www.musl-libc.org/
可以对glibc/uclibc-ng /musl进行对比测试:
1.静态编译hello.c程序并比较大小
使用gcc 6.3, armel, musl 1.1.16: 7300 字节
使用gcc 6.3, armel, uclibc-ng 1.0.22 : 67204 字节
使用gcc 6.2, armel, glibc: 492792 字节
2. 静态编译BusyBox 1.26.2并比较大小
使用gcc 6.3, armel, musl 1.1.16: 183348 字节
使用gcc 6.3, armel, uclibc-ng 1.0.22 : 210620 字节
使用gcc 6.2, armel, glibc: 755088 字节
3.2 指令集选择
编译rootfs进行测试对比:
用gcc 7.4编译,生成ARM代码:
根文件系统总大小:3.79 MB
用gcc 7.4编译,生成Thumb2代码:
根文件系统总大小:3.10 MB(-18%)
性能方面:Thumb2的性能明显改善(大约少于5%,但是从一次运行到另一次运行,测量的执行时间略有变化)。
4. 应用软件优化
4.1 测量strace
strace允许跟踪应用程序及其子级进行的所有系统调用。对于开发非常有用:
了解如何在用户空间上花费时间
例如,轻松查找打开尝试(open()),文件访问(read() /write() )和内存分配(mmap2() )。无需访问源代码即可完成!
寻找耗时最长的开销应用
查找在应用程序和脚本中完成的不必要的工作。例如:多次打开同一文件,或尝试打开不存在的文件。
局限性:您无法跟踪init进程!
关于strace 参见
http://sourceforge.net/projects/strace/:
在所有GNU / Linux系统上可用,可以由您的交叉编译工具链生成器构建。
更简单的办法:直接拷贝一个现成的静态二进制文件。 参见
https://github.com/bootlin/static-binaries/tree/master/strace
可以查看进程的操作情况:
1. 访问文件,分配内存。。.
2. 通常足以发现简单的错误。
用法:
1.strace 《命令》(开始一个新进程)
2.strace -p 《pid》(跟踪现有进程)strace -c 《command》(统计进程的系统开销时间)
如查看cat操作:
4.2 Linux上的性能监测工具oprofile
Oprofile是linux上的性能监测工具:
具有两种工作方式:legacy模式和perf_events模式
legacy模式:
1.精度低,请使用内核驱动程序进行配置
2.使用CONFIG_OPROFILE进行编译配置
3.用户空间工具:opcontrol和oprofiled
perf_events模式:
1.使用硬件性能计数器
2.使用CONFIG_PERF_EVENTS和CONFIG_HW_PERF_EVENTS编译配置
3.用户空间工具:operf
其使用方法:
legacy 模式:
opcontrol --vmlinux=/path/to/vmlinux # optional step
opcontrol --start
/my/command
opcontrol --stop
perf_events 模式
operf --vmlinux=/path/to/vmlinux /my/command
利用opreport获取结果
4.3 perf工具
Perf 是内置于Linux 内核源码树中的性能剖析(profiling)工具。它基于事件采样原理,以性能事件为基础,支持针对处理器相关性能指标与操作系统相关性能指标的性能剖析。可用于性能瓶颈的查找与热点代码的定位。linux2.6及后续版本都自带该工具,几乎能够处理所有与性能相关的事件
使用硬件性能计数器
使用CONFIG_PERF_EVENTS和CONFIG_HW_PERF_EVENTS进行配置
用户空间工具:性能。它是内核源代码的一部分,因此始终与您的内核同步。
用法:perf record /my/command
通过以下方式获得结果:perf report
4.4 连接器优化
启动时使用的应用程序组代码:
查找启动期间调用的功能,例如使用
-finstrument-functions gcc选项。
创建一个自定义的链接描述文件,以按调用顺序重新排列这些函数。可以通过将每个函数放在各自的部分中来实现:
-ffunction-sections gcc选项。
特别对于具有较大MTD读取块的闪存存储特别有用。因为读取整个读取块后,极有可能读取不必要的数据。
详细信息:http://blogs.linux.ie/caolan/2007/04/24/controlling-symbol-ordering/
通过如下方法,可以找到有望被优化的地方:
1.启动一次应用程序并测量其启动时间。
2.再次启动应用程序并测量其启动时间。由于它的代码应仍在Linux文件缓存中,故其代码加载时间将为零。
从而知道第一次加载应用程序代码(及其库)所花费的时间。链接器优化节省的时间应少于此上限。
然后据此可以决定是否有必要这样对该应用进行链接优化。由于链接优化必须修改应用程序的编译方式,因此此类优化的成本很高。
4.4.1 Prelink 预链接工具
Prelink是Red Hat 开发者 Jakub Jelinek 所设计的工具,正如其名字所示,Prelink利用事先链接代替运行时链接的方法来加速共享库的加载,它不仅可以加快起动速度,还可以减少部分内存开销,是各种Linux架构上用于减少程序加载时间、缩短系统启动时间和加快应用程序启动的很受欢迎的一个工具。
预链接减少了启动可执行文件所需的时间
在Android上广泛使用
必须配置为知道哪些库需要进行预链接,并将为每个可用符号分配一个固定的地址,从而消除了在启动可执行文件时重新定位符号的需要。
请注意安全性,因为可执行代码始终加载在同一地址。
代码以及文档参见
http://people.redhat.com/jakub/prelink/
支持ARM,但自2013年以来未发布。Buildroot也不支持。但是,x86比较容易实现。
工具链/应用程序优化的部分就在此结束了,下篇我们将继续讲嵌入式Linux启动时间优化的方法之文件系统,请大家继续关注我们电子发烧友网和嵌入式客栈。