嵌入式Linux启动优化手记2 U…

转载 2013年12月02日 09:12:13
参考一下

既然不能使用新的U-boot,那就优化一点是一点,慢慢干吧。

1.去掉启动时的按键等待

U-boot 启动的时候出现一个 Hit any key to stop autoboot 不爽,干吗要停上1秒?虽然可以通过设置参数bootdelay=0来关掉这个延时,但这样做了以后就再也进不去U-boot了,更烦。检查代码,发现是在main.c函数int abortboot(int bootdelay)来干这个活的,好吧,改掉它

static __inline__ int abortboot(int bootdelay)
    {
        int abort = 0;
        char inputkey;

    if (tstc())
        
            inputkey = getc(); 
            abort = (inputkey == 'u');

    }
#ifdef CONFIG_SILENT_CONSOLE
       if (abort)
          gd->flags &= ~GD_FLG_SILENT;
#endif
        return abort;
   }

这样,就不需要等待了,如果想进入U-boot,就在上电的时候按住u吧,把它改成一个固定的键而不是任意键,因为串口线很容易受到干扰,如果是任意键的话,运行时即使不想进去有时也会进入U-boot的命令行。 

2.去掉网卡的初始化

每次上电,U-boot 都会初始化网卡,其实这根本不需要,把配置文件中

#define CONFIG_MII   1

去掉,启动时就不会初始化了,需要使用TFTP时,它会自动初始化,又节省了3.4秒的启动时间。

3.智能读取OS Image

U-boot 通过nand read 来读取OS Image,通常为了避免麻烦,我们设置的读取长度要比实际OS长度长一些,多读的那部分纯粹是浪费CPU时间,能不能精确判断读取长度呢,当然可以,为了不影响系统的正常功能,扩展一个nand read.os 指令来读取,修改方法如下:

在 nand_read_options_t 里面添加一个成员 int is_os_img

在函数 int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])

修改读操作的判断语句,添加 !strcmp(s, ".os"),然后设置opts.is_os_img = !strcmp(s, ".os");大概修改后结果参考第7步代码。

    最后,在函数int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts)中修改

代码,检测如果opts->is_os_img == 1 并且是第一次读取(2024B)之后,检查度取得结果是否是OS Image,如果是更新需要读取的长度,否则,也不需要再往下读了,直接返回错误就可以了[转载]嵌入式Linux启动优化手记2 <wbr>U-boot优化
                image_header_t  *hdr = (image_header_t *)buffer;     
                if (image_check_magic(hdr) && image_check_hcrc (hdr))
                {
                    size_t ossz = uimage_to_cpu(hdr->ih_size);//+ image_get_header_size();
                    imglen = ossz + + image_get_header_size();
                    printf("## Find valid OS image, at 0x%x, Size: %d Bytes = %d KBn",
                           (unsigned int)mtdoffset, ossz, ossz/1024);
                }
                else
                {
                    printf("Invalid OS image at 0x%xn", (unsigned int)mtdoffset);
                    return -1;
                }

4.去掉OS Image 内存复制过程

使用 nand read 读取OS Image 后,U-boot 使用 bootm 指令来启动Linux,检查其实现代码

int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

发现他会把已经读取到内存中的OS Image 在复制到一个指定的位置,OS Image 中的头部参数,这个值一般是固定的,本系统使用的是 0x70008000, 如果在 nand read 时读到的内存位置恰好合适,就可以省掉这些毫秒数了,做法如下:

nand read.os 0x70007FC0 0x100000 0x500000

(其中 0x70007FC0 = 0x70008000 - sizeof(sizeof(image_header_t)))

然后在内存复制的地方(函数do_bootm中),加入修改,跳过内存复制

    switch (comp) {
    case IH_COMP_NONE:
        if (load_start == (ulong)os_hdr) {
            printf ("   XIP %s ... ", type_name);
        } else {
            if (load_start != os_data)//位置不匹配,依然移动,否则就跳过此部
            {
                printf ("   Loading %s ... ", type_name);
                memmove_wd ((void *)load_start, (void *)os_data, os_len, CHUNKSZ);
                puts("OKn");
            }
        }
        load_end = load_start + os_len;

对于我们的Kernel,修改后大小是1.4M,省去这个搬移过程,节省了大约800ms的时间

5.减少内存初始化的时间

在U-boot 初始化时,在 start_armboot 函数中,多次使用到了 memset函数,其中最耗时的是在mem_malloc_init函数中调用memset 初始化 512K内存的调用,检查U-boot 1.3.4对memset的实现,发现是最简单的字节复制,把它改为按32bits复制的方式,这些memset 调用所花费的时间立即从202ms减少到了45ms

修改方法,再 string.c 中,找到memset函数,修改其实现(代码是从U-boot 2011.12 中复制过来的[转载]嵌入式Linux启动优化手记2 <wbr>U-boot优化

void * memset(void * s,int c,size_t count)
    {
        unsigned long *sl = (unsigned long *) s;
        unsigned long cl = 0;
        char *s8;
        int i;
  
        if ( ((ulong)s & (sizeof(*sl) - 1)) == 0) {
            for (i = 0; i < sizeof(*sl); i++) {
                cl <<= 8;
                cl |= c & 0xff;
            }
            while (count >= sizeof(*sl)) {
                *sl++ = cl;
                count -= sizeof(*sl);
            }
        }
   
        s8 = (char *)sl;
        while (count--)
            *s8++ = c;
        return s;
    }

6.减少NAND初始化时间
    每次 U-boot 启动,发现NAND初始化需要大约3秒的时间,仔细追踪发现,在nand_base.c文件中nand_scan函数的最后一步return this->scan_bbt (mtd);最花费时间,这个scan_bbt扫描整个NAND并检查坏块,重建坏块表,在启动过程中,这个耗时的操作毫无意义,去掉这一步,让nand_scan 函数直接返回0就可以了。

7.添加Yaffs2支持

从网上各位前辈的论述中,都发现YAFFS比JFFS2要快,也决定测试一下,从YAFFS网站下载最新的代码,按照说明加入到Linux 中,重新编译内核,让内核支持YAFFS2(按照默认的选项就可以了),弄一个空的分区,格式化成YAFFS2格式,感觉的确比较快,把ROOTFS复制到这个分区,然后修改Linux启动参数让它把YAFFS2分区当作根分区启动,发现果然快了不少,初始化和挂载根分区仅需要370ms,比JFFS2的速度快多了,决定就采用YAFFS2作为根文件系统了。自己在u-boot中添加对yaffs2 image的支持

说起来容易,真正做起来还是很麻烦的,总是不能把yaffs2的image 烧写成功,不知道是Image不正确还是Uboot没改对,折腾了几天也没搞定,最后终于发现了一个第三方的工具

http://code.google.com/p/yaffs2utils/

下载,编译,制作Image,验证,OK,把新工具生成的IMAGE与YAFFS2自带的工具对照,发现YAFFS2自带的工具生成的IMAGE不正确,晕死。

重新修改UBoot,改了很少一部份代码,就可以了。

依然是在函数do_nand中修改,添加一个扩展 nand write.y 指令来写入Image:

按照惯例,YAFFS2的第一个块不使用,留给文件系统自己使用,在 nand_write_options_t 里面添加一个成员 int skip_first_block;

在函数 int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])

修改读写操作的判断语句,添加 !strcmp(s, ".y"),然后设置opts.is_os_img = !strcmp(s, ".os");大概修改后结果如下(红色部分)

    s = strchr(cmd, '.');
        if (s != NULL && (!strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i") || !strcmp(s, ".os") || !strcmp(s, ".y")))     {
            if (read) {
               
                nand_read_options_t opts;
                memset(&opts, 0, sizeof(opts));
                opts.buffer    = (u_char*) addr;
                opts.length    = size;
                opts.offset    = off;
                opts.readoob = 0;//remove this function.
                opts.is_os_img = !strcmp(s, ".os");
                opts.quiet      = quiet;
                ret = nand_read_opts(nand, &opts);
                //printf("call nand_read_opts buffer %lu len %lu offset %d off, ret %dn", addr, size, off, ret);
            } else {
               
                nand_write_options_t opts;
                memset(&opts, 0, sizeof(opts));
                opts.buffer    = (u_char*) addr;
                opts.length    = size;
                opts.offset    = off;
               
                if (!strcmp(s, ".y"))
                {
                    opts.pad    = 0;
                    opts.writeoob = 1;                 
                    //opts.noecc = 1;
                    opts.skip_first_block = 1;
                    opts.autoplace = 1;
                }
                else
                 
                    opts.pad    = 1;
                }
                opts.blockalign = 1;                 
                opts.quiet      = quiet;
                ret = nand_write_opts(nand, &opts);
            }
        } else if (s != NULL && !strcmp(s, ".oob")) {...}

在函数nand_write_opts中相应修改

int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)
    {
          int yaffs_skip_first = opts->skip_first_block;

      ...

      while ((imglen > 0) && (mtdoffset < meminfo->size)) {

             ...
              while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) {
                  do {
                      ...
                  } while (offs < blockstart + erasesize_blockalign);

          }

          if (yaffs_skip_first)
              {
                   yaffs_skip_first = 0;
                   mtdoffset += erasesize_blockalign;
                   continue;
               }

           readlen = meminfo->oobblock;
               if (opts->pad && (imglen < readlen)) 

           ...
          }

      ...

}

8. 其它一些优化措施

经过这些折腾之后,整个系统的启动时间大大加快,然后优化Linux自身的一些启动瓶颈

Linux的启动参数优化:加上了 lpj=99072,节约了几十个毫秒,加上quiet,节约了大约1秒时间

修改内核编译选项,把不需要的内核模块干掉

最后,Linux自身的启动速度约为1.1秒,整个系统的启动速度大约4秒多一点,初步达到了优化目标,系统的主要延时发生在U-boot 1.3.4的FLASH读取上,FLASH读取速度大约只有600KB/S。尝试把Uboot 2011.12的FLASH驱动移植到U-boot 1.3.4上,花费了几天时间,终于可以编译成功了,可惜经常出一些莫名其妙的错误,太不稳定,只好放弃。

以我的能力,U-boot优化到这里就到头了,正准备结束工作时,发现了另外一条可以加速系统启动的方法
可以继续尝试,让我最终把系统的启动时间减少到了1.7秒。

请看下篇[转载]嵌入式Linux启动优化手记2 <wbr>U-boot优化

相关文章推荐

嵌入式Linux启动优化手记2 U-boot优化

既然不能使用新的U-boot,那就优化一点是一点,慢慢干吧。 1.去掉启动时的按键等待 U-boot 启动的时候出现一个 Hit any key to stop autoboot 不爽,干吗要停上...

构建U盘启动的嵌入式linux

1 构建U盘启动的嵌入式linux(一)前言:在参考了一书和独孤九贱的后,他们都是用CF卡来制作,对于大多数人估计和我一样,手上并没有板子和CF卡,而只有x86的电脑和USB盘,这里详细的描述一下我是...

USB无线网卡的嵌入式Linux驱动移植 - 手记

USB无线网卡的嵌入式Linux驱动移植 - 手记 应用场景:嵌入式Linux开发板要通过wifi上网,需要一个无线网卡和在开发板上加载安装相应的驱动。 【参考资料:】 ...
  • sonictl
  • sonictl
  • 2014年05月29日 21:38
  • 1905

水星MW54U无线网卡在嵌入式linux下驱动的配置问题

本篇基于友善之臂的mini2440 arm9开发平台,使用官方配套的根文件系统和内核树。       水星MW54U无线网卡是基于Rt2571无线网卡芯片的,属于ralink公司的rt73体系,和rt...

嵌入式Linux用mdev实现U盘自动挂载

ARM嵌入式Linux下,要让U盘自动挂载,可以用到mdev方式实现(mdev是busybox中的一个udev管理程序的一个精简版,它可以实现设备节点的自动创建和设备的自动挂载),下面是实现的步骤: ...

嵌入式linux下u盘自动挂载的实现

我实现的方法很简单,主要是周期读取文件/proc/partitions文件,当有u盘插入时,该文件中就会有u盘的信息:“sdb1、sdb2”等, 当系统检测到这些信息时,就执行命令mount,将u盘...

嵌入式linux下u盘自动挂载的实现

我实现的方法很简单,主要是周期读取文件/proc/partitions文件,当有u盘插入时,该文件中就会有u盘的信息:“sdb1、sdb2”等, 当系统检测到这些信息时,就执行命令mount,将u盘...

嵌入式linux下u盘升级的设计

一.内核配置,配置使其支持u盘 make menu_config     Device Drivers --->         [*]USB support -->                U...
  • paomadi
  • paomadi
  • 2013年07月11日 15:26
  • 3316

嵌入式linux下u盘升级的设计

一.内核配置,配置使其支持u盘 make menu_config     Device Drivers --->         [*]USB support -->             ...

嵌入式Linux启动过程(2)

3.Linux内核的启动过程 在 bootloader将 Linux 内核映像拷贝到 RAM 以后,可以通过下例代码启动 Linux 内核:call_linux(0, machine_type, k...
  • guowake
  • guowake
  • 2011年08月05日 12:48
  • 665
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:嵌入式Linux启动优化手记2&nbsp;U…
举报原因:
原因补充:

(最多只允许输入30个字)