Linux启动时间优化实战,2。41秒启动应用
劳动节,更个文吧,祝大家都劳有所获。
今天看了一个关于启动优化的讲座,简单总结一下。
本文的目标是尝试一些比较简单有效的方法,并不会覆盖所有的优化技巧。感兴趣的伙伴可以关注我视频号,后面准备用直播的方式和大家交流。
目标系统
硬件:
BeagleBoneBlack(CortexA8)
USB摄像头LCD
软件:
Linux5。1Buildrootrootfs
FFmpeg,用于采集视频并解码到LCD。
点击查看大图
当前启动时间:
从上电到LCD显示第一帧图像:9。45秒
1、优化编译器
ARMvsThumb2
比较基于ARM或者Thumb2指令集编译出来的系统和应用。
ARM:rootfs为3。79MB,ffmpeg为227KB。
Thumb2:3。10MB(18),183KB(19)。
性能方面:Thumb2的性能明显略有提升(约小于5)。
虽然性能有所提升,但是我个人还是会选择ARM指令集。
muslvsuClibc
Buildroot里有3种C库可以选择:glibc、musl、uClibc,这里我们只比较后面2种比较小巧的库。
musl:680KB(统计lib目录)。
uClibc:570KB(16)。
uClibc节省了110KB,我们选择uClibc。
2、优化应用程序
我们可以通过。configure对FFmpeg的功能组件进行选择。
另外,还可以用strace和perf命令调试以优化FFmpeg的内部d代码。
优化后的结果:
文件系统:从16。11MB缩小到3。54MB(78)。
程序的加载和运行时间:缩短150ms。
整体启动时间:缩短350ms。
在空间的优化很大,但是在启动时间上的优化很小,这是因为Linux运行程序时只加载程序的必要部分。
3、优化Init和根文件系统
思路:
使用bootchartd分析系统启动并裁剪不必要的服务。
将etcinit。d下的启动脚本合并为一个。
不挂载proc和sys。
裁剪BusyBox,文件系统越小,内核挂载可能会越快。
将Init程序替换成我们的应用程序。
静态编译应用程序。
裁剪掉不常用的文件,找出长时间不访问的文件:findatime1000typef
优化后的结果:
文件系统:裁剪Busybox后,从3。54MB缩小到2。33MB(34)。
启动时间:基本没改变,大概是因为文件系统本身就足够小了。
使用initramfs作为rootfs:
一般情况下,Linux系统会先挂载initramfs,initramfs很小且位于内存中,再由initramfs负责负载根文件系统。
当我们将Buildrootrootfs裁剪得很小时,就可以考虑直接将其作为initramfs使用。
这样有什么好处呢?
initramfs可以和Kernel拼接在一起,Bootloader负责将Kernelinitramfs加载到内存中,内核不再需要访问磁盘。
内核不再需要blockstorage和filesystem相关的功能,体积会变得更小,加载时间和初始化时间都会变小。
注意,需要关闭initramfs的压缩(CONFIGINITRAMFSCOMPRESSIONNONE)。
优化后的结果:
即便禁用了CONFIGBLOCK和CONFIGMMC后,总启动时间仍多了20ms。这可能是因为Kernelinitramfs拼在一起之后,内核变大了许多,而内核镜像是需要解压,解压的时间增多了。
4、优化内核
评估方法:
在启动参数里添加initcalldebug,能得到更多内核log:〔3。750000〕callingov2640i2cdriverinit0x00x101
〔3。760000〕initcallov2640i2cdriverinit0x00x10returned0after544usecs
〔3。760000〕callingat91sam9x5videoinit0x00x141
〔3。760000〕at91sam9x5videof0030340。lcdheo1:videodeviceregistered0xe0d3e340,irq24
〔3。770000〕initcallat91sam9x5videoinit0x00x14returned0after10388usecs
〔3。770000〕callinggspcainit0x00x181
〔3。770000〕gspcamain:v2。14。0registered
〔3。770000〕initcallgspcainit0x00x18returned0after3966usecs
。。。
另外,可以用scriptsbootgraph。pl将dmesg的信息转换成图片:scriptsbootgraph。plboot。logboot。svg
点击查看大图
接下来,找出消耗时间最多的环节,进行优化。
裁掉tracing
在Kernelhacking里关闭Tracers相关的功能。
启动时间:缩短550ms。
内核大小:缩小217KB。
裁掉一些用不上的硬件功能omap8250platformdriverinit(660ms)
cpswdriverinit(112ms)
am335xchildinit(82ms)
。。。
预设loopsperjiffy
在每次启动时,内核都会校准delayloop的值,用于udelay函数。
这会测量loopsperjiffy(lpj)的值。我们只需要启动一次内核,在log查找lpj值:Calibratingdelayloop。。。996。14BogoMIPS(lpj4980736)
然后将lpj4980736填写到启动参数中,即可:Calibratingdelayloop(skipped)presetvalue。。996。14BogoMIPS(lpj4980736)
大约缩短了82ms。
禁用CONFIGSMP
SMP的初始化很慢。它通常在默认配置中是启用的,即使是一个单核CPU。
如果我们的平台是单核的,可以禁用SMP。
关闭后,内核缩小:188KB(4。6),启动时间缩短126ms。
禁用log
启动参数里添加quiet,启动时间缩短577ms。
禁用CONFIGPRINTK和CONFIGBUG后,内核缩小118KB(5。8)。
禁用CONFIGKALLSYMS后,内核缩小107KB(5。7)。
合计,启动时间缩短767ms。
开启CONFIGEMBEDDED和CONFIGEXPERT
这会让系统调用变得更精简,内核会变得没那么通用,但是能保持你的应用程序能运行就足够了。
内核缩小51KB。
启动时间缩短34ms。
选择SLABmemoryallocators
一般是SLAB、SLOB、SLUB三选一。
SLAB:默认选择,最通用、最传统、最可靠。
SLOB:更简洁,代码量更少,更节省空间,适合嵌入式系统,使能后,内核缩小5KB,但是启动时间增加1。43S!
SLUB:更合适大型系统,使能后,启动时间增加2ms。
因此,我们仍使用SLAB。
内核压缩方式
不同压缩方式的特点如下:
实测效果:
看起来,gzip和lzo表现更好。测试的效果应该是和CPU磁盘的性能相关的。
内核编译参数
使能CONFIGCCOPTIMIZEFORSIZE,该选项可能是用gccOs代替gccO2。
点击查看大图
注意,这只是在BeagleBoneBlackLinux5。1上的测试结果,不同平台之间有差异。
禁用proc等伪文件系统
要考虑应用的兼容性。
ffmpeg依赖proc,所以只能关闭一些proc相关的选项:CONFIGPROCSYSCTL、CONFIGPROCPAGEMONITORCONFIGCONFIGFSFS,启动时间没有变化。
关闭sysfs,启动时间缩短35ms。
拼接DTB
启用CONFIGARMAPPENDEDDTB:catarcharmbootzImagearcharmbootdtsam335xboneblacklcd4。dtbzImage
setenvbootcmdfatloadmmc0:181000000zImage;bootz81000000
启动时间缩短26ms。
5、优化Bootloader
这里我们采用最好的方案:使用UbootFalconmode。
Falconmode只执行Uboot的第一阶段:SPL,然后跳过Stage2,执行加载Kernel。
启动时间缩短250ms。
总结
到此,启动优化基本完成,最终效果如下:〔0。0000000。000000〕
〔0。0007850。000785〕UBootSPL2019。01(Oct27201908:04:060100)
〔0。0578220。057822〕TryingtobootfromMMC1
〔0。3788780。321056〕fdtroot:FDTERRBADMAGIC
〔0。7753060。396428〕Waitingfordevvideo0tobeready。。。
〔1。9663671。191061〕Startingffmpeg
。。。
〔2。4122840。004277〕Firstframedecoded
从上电到LCD显示第一帧图像,总时间为2。41秒。
最有效果的步骤如下:
点击查看大图
仍值得优化的空间:
系统花了1。2秒等待USB摄像头的枚举,这里是否有办法加速?
是否可以关闭tty和终端登录?
最后,关于优化启动时间,有一些原则可以遵循:
请不要过早地进行优化。
从一些影响面最小的点开始优化。
从rootfs、kernel、bootloader自上而下进行优化。
重点关注短板。
感谢阅读,欢迎转发哦!