Linux系统启动时间的极限优化

时间:2007-12-18

  (1)首先是对Linux启动过程的跟踪和分析,生成详细的启动时间。 

  较为简单可行的方式是通过PrintkTime功能为启动过程的所有内核信息增加时间戳,便于汇总分析。PrintkTime早为CELF所提供的一个内核补丁,在后来的Kernel 2.6.11版本中正式纳入标准内核。所以大家可能在新版本的内核中直接启用该功能。如果你的Linux内核因为某些原因不能更新为2.6.11之后的版本,那么可以参考CELF提供的方法修改或直接它们提供的补丁:https://tree.celinuxforum.org/CelfPubWiki/PrintkTimes 

  开启PrintkTime功能的方法很简单,只需在内核启动参数中增加“time”即可。当然,你也可以选择在编译内核时直接指定“Kernel hacking”中的“Show timing information on printks”来强制每次启动均为内核信息增加时间戳。这一种方式还有另一个好处:你可以得到内核在解析启动参数前所有信息的时间。因此,我选择后一种方式。 

  当完成上述配置后,重新启动Linux,然后通过以下命令将内核启动信息输出到文件: 

  dmesg -s 131072 > ktime 

  然后利用一个脚本“show_delta”(位于Linux源码的scripts文件夹下)将上述输出的文件转换为时间增量显示格式: 

  /usr/src/linux-x.xx.xx/scripts/show_delta ktime > dtime 

  这样,你就得到了一份关于Linux启动时间消耗的详细。 

  (2)然后,我们就来通过这份,找出启动中相对耗时的过程。 

  必须明确一点:中的时间增量和内核信息之间没有必然的对应关系,真正的时间消耗必须从内核源码入手分析。 

  这一点对于稍微熟悉编程的朋友来说都不难理解,因为时间增量只是两次调用printk之间的时间差值。通常来说,内核启动过程中在完成一些耗时的任务,如创建hash索引、probe硬件设备等操作后会通过printk将结果打印出来,这种情况下,时间增量往往反映的是信息对应过程的耗时;但有些时候,内核是在调用printk输出信息后才开始相应的过程,那么中内核信息相应过程的时间消耗对应的是其下一行的时间增量;还有一些时候,时间消耗在了两次内核信息输出之间的某个不确定的时段,这样时间增量可能就完全无法通过内核信息反应出来了。 

  所以,为了准确判断真正的时间消耗,我们需要结合内核源码进行分析。必要的时候,例如上述第三种情形下,还得自己在源码中插入printk打印,以进一步确定实际的时间消耗过程。 

  以下是我上次裁减后Linux内核的启动分析: 

  内核启动总时间: 6.188s 

  关键的耗时部分: 

  1) 0.652s - Timer,IRQ,Cache,Mem Pages等部分的初始化 

  2) 0.611s - 内核与RTC时钟同步 

  3) 0.328s - 计算Calibrating Delay(4个CPU的总消耗) 

  4) 0.144s - 校准APIC时钟 

  5) 0.312s - 校准Migration Cost 

  6) 3.520s - Intel E1000网卡初始化 

  下面,将针对上述各部分进行逐一分析和化解。 

  (3)接下来,进行具体的分项优化。 

  CELF已经提出了一整套针对消费类电子产品所使用的嵌入式Linux的启动优化方案,但是由于面向不同应用,所以我们只能部分借鉴他们的经验,针对自己面对的问题作出具体的分析和尝试。 

  内核关键部分(Timer、IRQ、Cache、Mem Pages……)的初始化目前暂时没有比较可靠和可行的优化方案,所以暂不考虑。 

  对于上面分析结果中的 2、3 两项,CELF已有专项的优化方案:“RTCNoSync”和“PresetLPJ”。 

  前者通过屏蔽启动过程中所进行的RTC时钟同步或者将这一过程放到启动后进行(视具体应用对时钟的需求而定),实现起来比较容易,但需要为内核打补丁。似乎CELF目前的工作仅仅是去掉了该过程,而没有实现所提到的“延后”处理RTC时钟的同步。考虑到这个原因,我的方案中暂时没有引入这一优化(毕竟它所带来的时间漂移已经达到了“秒”级),继续关注中。 

  后者是通过在启动参数中强制指定LPJ值而跳过实际的计算过程,这是基于LPJ值在硬件条件不变的情况下不会变化的考虑。所以在正常启动后记录下内核信息中的“Calibrating Delay”数值后就可以在启动参数中以下面的形式强制指定LPJ值了: 


  lpj=9600700 

  上面分析结果中的 4、5 两项都是SMP初始化的一部分,因此不在CELF研究的范畴(或许将来会有采用多核的MP4出现?……),只能自力更生了。研究了一下SMP的初始化代码,发现“Migration Cost”其实也可以像“Calibrating Delay”采用预置的方式跳过校准时间。方法类似,在内核启动参数中增加: 

  migration_cost=4000,4000 

  而Intel的网卡驱动初始化优化起来就比较麻烦了,虽然也是开源,但读硬件驱动完全不比读一般的C代码,况且建立在如此肤浅理解基础上的“优化”修改也实在难保万全。基于可靠性的考虑,我终在两次尝试均告失败后放弃了这一条路。那么,换一个思维角度,可以借鉴CELF在“ParallelRCScripts”方案中的“并行初始化”思想,将网卡驱动独立编译为模块,放在初始化脚本中与其它模块和应用同步加载,从而消除Probe阻塞对启动时间的影响。考虑到应用初始化也可能使用到网络,而在我们的实际硬件环境中,只有eth0是供应用使用的,因此需要将个网口初始化的0.3s时间计算在内。 

  除了在我的方案中所遇到的上述各优化点,CELF还提出了一些你可能会感兴趣的有特定针对性的专项优化,如: 

  ShortIDEDelays - 缩短IDE探测时长(我的应用场景中不包含硬盘,所以用不上) 

  KernelXIP - 直接在ROM或Flash中运行内核(考虑到兼容性因素,未采用) 

  IDENoProbe - 跳过未连接设备的IDE口 

  OptimizeRCScripts - 优化initrd中的linuxrc脚本(我采用了BusyBox更简洁的linuxrc) 

  以及其它一些尚处于设想阶段的优化方案,感兴趣的朋友可以访问CELF Developer Wiki了解详情。 

  (4)优化结果 

  经过上述专项优化,以及对inittab、rcS脚本的冗余裁减,整个Linux内核的启动时间从优化前的 6.188s 下降到了终的 2.016s,如果不包含eth0的初始化,则仅需 1.708s(eth0初始化可以和系统中间件及部分应用加载并行),基本达到了既定目标。与Kexec配合,可以大大降低软件故障导致的复位时间,有效的提升了产品的可靠性。 



  
上一篇:面向高性能、低耗电及变频白色家电的32位微控制器MB91480系列
下一篇:BMW轿车音响I2C总线控制多路音源电路

免责声明: 凡注明来源本网的所有作品,均为本网合法拥有版权或有权使用的作品,欢迎转载,注明出处。非本网作品均来自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

相关技术资料