内核启动时间优化一例

http://blog.csdn.net/zjujoe/article/details/5594961

内核启动时间优化一例

作者: 宋立新

Email:zjujoe@yahoo.com

前言

       为了提高开机时间,我们需要优化的部分有:

1)    bootloader 启动速度

2)    Linux 内核启动速度

3)    文件系统启动速度

4)    Nand (假设你的 Storage 使用 nand)的读速度

5)    应用框架启动速度

等等。

 

本文的内容为自己最近对某内核做的一个优化。供参考。

 

背景知识

       Linux 内核的执行起点在 (arch/arm/kernel/head.S), 首先会初始化硬件(所做不多,因为很多操作 bootloader 已经完成), 获取 cpu id, machine id, 设置少量的页表,然后打开 MMU。清空 BSS 后, 会跳转到 C 语言函数 start_kernel

 

Start_kernel 调了很多早期初始化函数, 然后会调 用 rest_init.

 

Rest_init 函数中, 会创建 init 线程,并调度它, 该线程最 终调用用户空间的 init 程序, 进化为一切其他用户程序的祖宗进程。

 

原来的 rest_init 会继续执行,它先通过手工方式,把自己演变成0号进程, 然后调用 cpu_idle 函数, 此函数乃一 while(1){} 循环, 永不退 出!

 

这样, 系统启动显示运行0号进程,然后运行 1 号进程, 只有在系 统没事可干时, 才会调用 0 号进程, 所以, 0 号进程又叫 idle 进程。

 

对于我们来说,主要的任务(很多很多的 init 函数调用)则是由 1 号进程 init 来完成的。 它的主函数为:kernel_init, 其中会调用do_basic_setup,该函数会调用do_initcalls,内核启动的大多数时间就花在该函数里。
 

准备

首先,我们打开CONFIG_PRINTK_TIME, 这样,内核的串口打印信息就带有时间信息,精度为 jiffies (1 – 10 ms). 这基本满足我们的需要了。
 
其次,由于 initcall 中调用了很多初始化函数,我们加入打印语句,以便能够判断出某段时间花在那个初始函数上。
 
diff --git a/init/main.c b/init/main.c
index 7ef6a1e..7642983 100755
--- a/init/main.c
+++ b/init/main.c
@@ -800,8 +800,10 @@ static void __init do_initcalls(void)
 {
        initcall_t *call;
 
 
-       for (call = __early_initcall_end; call < __initcall_end; call++)
+       for (call = __early_initcall_end; call < __initcall_end; call++) {
+               printk("=======================now will call :%p/n", call);
                do_one_initcall(*call);
+       }
 
 
        /* Make sure there is no pending stuff from the initcall sequence */
        flush_scheduled_work();

开始

准备工作做好后,我们就开始优化了。

 

编译好内核, 烧到手机中,开机, 取到串口打印信息。

 

然后就根据串口打印信息进行优化。

 

Action 1(节约0.18s)

第一个时间跨度发生在如下两行:

[    0.030000] Calibrating delay loop... 103.62 BogoMIPS (lpj=518144)

[    0.210000] Mount-cache hash table entries: 512

 

这段时间用于计算出 lpj (loops per jiffies), 我们可以通过在命令行预设该值,从而省掉这段时间:

--- a/arch/arm/configs/android2010_defconfig
+++ b/arch/arm/configs/android2010_defconfig
@@ -250,7 +250,7 @@ CONFIG_ALIGNMENT_TRAP=y
 #
 CONFIG_ZBOOT_ROM_TEXT=0x0
 CONFIG_ZBOOT_ROM_BSS=0x0
-CONFIG_CMDLINE="console=ttyMTD2 console=ttyS0,115200n8 mem=104M ip=off mtdparts=evb1226-nand:4M(kernel),3M(r
+CONFIG_CMDLINE="console=ttyMTD2 console=ttyS0,115200n8 mem=104M lpj=518144 ip=off mtdparts=evb1226-nand:4M(k
 # CONFIG_XIP_KERNEL is not set
 # CONFIG_KEXEC is not set
 

编译,执行,发现这段时间没有了:

[    0.030000] Calibrating delay loop (skipped) preset value.. 103.62 BogoMIPS (lpj=518144)

[    0.030000] Mount-cache hash table entries: 512

 

这样,我们通过预设 lpj, 优化了 0.18  秒。

 

Action 2(节约0s)

[    0.200000] =======================now will call :c00232ec

  0.420000] =======================now will call :c00232f0

 

查看c00232ec, 在 System.map 查询该函数,名为:populate_rootfs
其会调用:unpack_to_rootfs
其又会调用:gunzip

 

这个动作为解压缩 initramfs, 所以,

 

这 0.22 秒时间是必须的,无法优化。

 

Action 3(节约0.15s)

[    0.440000] =======================now will call :c00233e8

[    0.450000] IM9815 s6b33bl LCM

[    0.670000] =======================now will call :c00233ec

c00233e8 对应文件 im9815fb_init 仔细查看 lcm 驱动,  发现其中有一些 mdelay, 试着减少这些 delay, 并保证 lcm 功能不收影响, 大大减少了 lcm 的时延。

[    0.440000] =======================now will call :c00233e8

[    0.440000] IM9815 s6b33bl LCM

[    0.490000] =======================now will call :c00233ec

这样,我们通过优化 lcm 驱动, 优化了 0.15  秒。

 

Action 4(节约0.06s)

<4>[    0.490000] =======================now will call :c00233f0

<4>[    0.570000] =======================now will call :c00233f4

<4>[    0.620000] =======================now will call :c00233f8

 

c00233f0 对应函数tty_init,

c00233f4 对应函数pty_init,

仔细分析, 这两个函数会注册一些 tty/pty 设备, 其中 tty 为 63 个, pty 为 16 个。

我们认为63个 tty 设备没有必要,于是修改了相关定义:

--- a/include/linux/vt.h

+++ b/include/linux/vt.h

@@ -18,8 +18,8 @@ extern int unregister_vt_notifier(struct notifier_block *nb);

  * resizing).

  */

 #define MIN_NR_CONSOLES 1       /* must be at least 1 */

-#define MAX_NR_CONSOLES        63      /* serial lines start at 64 */

-#define MAX_NR_USER_CONSOLES 63        /* must be root to allocate above this */

+#define MAX_NR_CONSOLES        15      /* serial lines start at 16 */

+#define MAX_NR_USER_CONSOLES 15        /* must be root to allocate above this */

 

修改完后,发现节约了 0.06s:

 

<4>[    0.490000] =======================now will call :c00233f0

<4>[    0.510000] =======================now will call :c00233f4

<4>[    0.550000] =======================now will call :c00233f8

Action 5(节约1s)

[    0.570000] =======================now will call :c0023408

[    0.570000] Serial: 8250/16550 driver, 3 ports, IRQ sharing enabled

[    0.570000] serial8250.0: ttyS0 at MMIO 0xf0010000 (irq = 5) is a 16550A

[    0.570000] Port 0: Set UART ECR baud: 115200

[    0.570000] console [ttyS0] enabled

[    1.580000] serial8250.0: ttyS1 at MMIO 0xf0011000 (irq = 5) is a 16550A

[    1.590000] serial8250.0: ttyS2 at MMIO 0xf0012000 (irq = 5) is a 16550A

[    1.600000] =======================now will call :c002340c

 

c0023408 对应函数:serial8250_init, 仔细研究发现,这里的一秒钟时间主要是因为系统第一次注册 console 时,内核会把积累在log buffer 里的信息输出到这个 console, 优化的方法是在 cmdline 中加 quiet 参数:

CONFIG_CMDLINE="console=ttyMTD2 console=ttyS0,115200n8 mem=104M lpj=518144 quiet ip=off mtdparts=evb1226-nand:4M(kernel),3M(ramdisk),1M(panic),72M(system),4M(cache),-(userdata) init=/init"

 

修改以后, 1秒时延没有了:

<4>[    0.640000] =======================now will call :c0023408

<6>[    0.640000] Serial: 8250/16550 driver, 3 ports, IRQ sharing enabled

<6>[    0.640000] serial8250.0: ttyS0 at MMIO 0xf0010000 (irq = 5) is a 16550A

<4>[    0.640000] Port 0: Set UART ECR baud: 115200

<6>[    0.640000] console [ttyS0] enabled

<6>[    0.650000] serial8250.0: ttyS1 at MMIO 0xf0011000 (irq = 5) is a 16550A

<6>[    0.650000] serial8250.0: ttyS2 at MMIO 0xf0012000 (irq = 5) is a 16550A

<4>[    0.660000] =======================now will call :c002340c

 

总结

其它方法还有: 裁剪内核,删除不需要的代码、打印信息。 等等。

当然,任何时候,优化的代码是最重要的!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值