既然不能使用新的U-boot,那就優(yōu)化一點(diǎn)是一點(diǎn),慢慢干吧。
1.去掉啟動(dòng)時(shí)的按鍵等待
U-boot 啟動(dòng)的時(shí)候出現(xiàn)一個(gè) Hit any key to stop autoboot 不爽,干嗎要停上1秒?雖然可以通過(guò)設(shè)置參數(shù)bootdelay=0來(lái)關(guān)掉這個(gè)延時(shí),但這樣做了以后就再也進(jìn)不去U-boot了,更煩。檢查代碼,發(fā)現(xiàn)是在main.c函數(shù)int abortboot(int bootdelay)來(lái)干這個(gè)活的,好吧,改掉它
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;
? ?}
這樣,就不需要等待了,如果想進(jìn)入U(xiǎn)-boot,就在上電的時(shí)候按住u吧,把它改成一個(gè)固定的鍵而不是任意鍵,因?yàn)榇诰€很容易受到干擾,如果是任意鍵的話(huà),運(yùn)行時(shí)即使不想進(jìn)去有時(shí)也會(huì)進(jìn)入U(xiǎn)-boot的命令行。?
2.去掉網(wǎng)卡的初始化
每次上電,U-boot 都會(huì)初始化網(wǎng)卡,其實(shí)這根本不需要,把配置文件中
#define CONFIG_MII ? 1
去掉,啟動(dòng)時(shí)就不會(huì)初始化了,需要使用TFTP時(shí),它會(huì)自動(dòng)初始化,又節(jié)省了3.4秒的啟動(dòng)時(shí)間。
3.智能讀取OS Image
U-boot 通過(guò)nand read 來(lái)讀取OS Image,通常為了避免麻煩,我們?cè)O(shè)置的讀取長(zhǎng)度要比實(shí)際OS長(zhǎng)度長(zhǎng)一些,多讀的那部分純粹是浪費(fèi)CPU時(shí)間,能不能精確判斷讀取長(zhǎng)度呢,當(dāng)然可以,為了不影響系統(tǒng)的正常功能,擴(kuò)展一個(gè)nand read.os 指令來(lái)讀取,修改方法如下:
在 nand_read_options_t 里面添加一個(gè)成員 int is_os_img
在函數(shù) int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
修改讀操作的判斷語(yǔ)句,添加 !strcmp(s, ".os"),然后設(shè)置opts.is_os_img = !strcmp(s, ".os");大概修改后結(jié)果參考第7步代碼。
? ? 最后,在函數(shù)int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts)中修改
代碼,檢測(cè)如果opts->is_os_img == 1 并且是第一次讀取(2024B)之后,檢查度取得結(jié)果是否是OS Image,如果是更新需要讀取的長(zhǎng)度,否則,也不需要再往下讀了,直接返回錯(cuò)誤就可以了嵌入式Linux啟動(dòng)優(yōu)化手記2 U-boot優(yōu)化
? ? ? ? ? ? ? ? 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 KB
",
? ? ? ? ? ? ? ? ? ? ? ? ? ?(unsigned int)mtdoffset, ossz, ossz/1024);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? printf("Invalid OS image at 0x%x
", (unsigned int)mtdoffset);
? ? ? ? ? ? ? ? ? ? return -1;
? ? ? ? ? ? ? ? }
4.去掉OS Image 內(nèi)存復(fù)制過(guò)程
使用 nand read 讀取OS Image 后,U-boot 使用 bootm 指令來(lái)啟動(dòng)Linux,檢查其實(shí)現(xiàn)代碼
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
發(fā)現(xiàn)他會(huì)把已經(jīng)讀取到內(nèi)存中的OS Image 在復(fù)制到一個(gè)指定的位置,OS Image 中的頭部參數(shù),這個(gè)值一般是固定的,本系統(tǒng)使用的是 0x70008000, 如果在 nand read 時(shí)讀到的內(nèi)存位置恰好合適,就可以省掉這些毫秒數(shù)了,做法如下:
nand read.os 0x70007FC0 0x100000 0x500000
(其中 0x70007FC0 = 0x70008000 - sizeof(sizeof(image_header_t)))
然后在內(nèi)存復(fù)制的地方(函數(shù)do_bootm中),加入修改,跳過(guò)內(nèi)存復(fù)制
? ? switch (comp) {
? ? case IH_COMP_NONE:
? ? ? ? if (load_start == (ulong)os_hdr) {
? ? ? ? ? ? printf (" ? XIP %s ... ", type_name);
? ? ? ? } else {
? ? ? ? ? ? if (load_start != os_data)//位置不匹配,依然移動(dòng),否則就跳過(guò)此部
? ? ? ? ? ? {
? ? ? ? ? ? ? ? printf (" ? Loading %s ... ", type_name);
? ? ? ? ? ? ? ? memmove_wd ((void *)load_start, (void *)os_data, os_len, CHUNKSZ);
? ? ? ? ? ? ? ? puts("OK
");
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? load_end = load_start + os_len;
對(duì)于我們的Kernel,修改后大小是1.4M,省去這個(gè)搬移過(guò)程,節(jié)省了大約800ms的時(shí)間
5.減少內(nèi)存初始化的時(shí)間
在U-boot 初始化時(shí),在 start_armboot 函數(shù)中,多次使用到了 memset函數(shù),其中最耗時(shí)的是在mem_malloc_init函數(shù)中調(diào)用memset 初始化 512K內(nèi)存的調(diào)用,檢查U-boot 1.3.4對(duì)memset的實(shí)現(xiàn),發(fā)現(xiàn)是最簡(jiǎn)單的字節(jié)復(fù)制,把它改為按32bits復(fù)制的方式,這些memset 調(diào)用所花費(fèi)的時(shí)間立即從202ms減少到了45ms
修改方法,再 string.c 中,找到memset函數(shù),修改其實(shí)現(xiàn)(代碼是從U-boot 2011.12 中復(fù)制過(guò)來(lái)的嵌入式Linux啟動(dòng)優(yōu)化手記2 U-boot優(yōu)化)
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初始化時(shí)間
? ? 每次 U-boot 啟動(dòng),發(fā)現(xiàn)NAND初始化需要大約3秒的時(shí)間,仔細(xì)追蹤發(fā)現(xiàn),在nand_base.c文件中nand_scan函數(shù)的最后一步return this->scan_bbt (mtd);最花費(fèi)時(shí)間,這個(gè)scan_bbt掃描整個(gè)NAND并檢查壞塊,重建壞塊表,在啟動(dòng)過(guò)程中,這個(gè)耗時(shí)的操作毫無(wú)意義,去掉這一步,讓nand_scan 函數(shù)直接返回0就可以了。
7.添加Yaffs2支持
從網(wǎng)上各位前輩的論述中,都發(fā)現(xiàn)YAFFS比JFFS2要快,也決定測(cè)試一下,從YAFFS網(wǎng)站下載最新的代碼,按照說(shuō)明加入到Linux 中,重新編譯內(nèi)核,讓內(nèi)核支持YAFFS2(按照默認(rèn)的選項(xiàng)就可以了),弄一個(gè)空的分區(qū),格式化成YAFFS2格式,感覺(jué)的確比較快,把ROOTFS復(fù)制到這個(gè)分區(qū),然后修改Linux啟動(dòng)參數(shù)讓它把YAFFS2分區(qū)當(dāng)作根分區(qū)啟動(dòng),發(fā)現(xiàn)果然快了不少,初始化和掛載根分區(qū)僅需要370ms,比JFFS2的速度快多了,決定就采用YAFFS2作為根文件系統(tǒng)了。自己在u-boot中添加對(duì)yaffs2 image的支持
說(shuō)起來(lái)容易,真正做起來(lái)還是很麻煩的,總是不能把yaffs2的image 燒寫(xiě)成功,不知道是Image不正確還是Uboot沒(méi)改對(duì),折騰了幾天也沒(méi)搞定,最后終于發(fā)現(xiàn)了一個(gè)第三方的工具
http://code.google.com/p/yaffs2utils/
下載,編譯,制作Image,驗(yàn)證,OK,把新工具生成的IMAGE與YAFFS2自帶的工具對(duì)照,發(fā)現(xiàn)YAFFS2自帶的工具生成的IMAGE不正確,暈死。
重新修改UBoot,改了很少一部份代碼,就可以了。
依然是在函數(shù)do_nand中修改,添加一個(gè)擴(kuò)展 nand write.y 指令來(lái)寫(xiě)入Image:
按照慣例,YAFFS2的第一個(gè)塊不使用,留給文件系統(tǒng)自己使用,在 nand_write_options_t 里面添加一個(gè)成員 int skip_first_block;
在函數(shù) int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
修改讀寫(xiě)操作的判斷語(yǔ)句,添加 !strcmp(s, ".y"),然后設(shè)置opts.is_os_img = !strcmp(s, ".os");大概修改后結(jié)果如下(紅色部分)
? ? 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 %d
", 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")) {...}
在函數(shù)nand_write_opts中相應(yīng)修改
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. 其它一些優(yōu)化措施
經(jīng)過(guò)這些折騰之后,整個(gè)系統(tǒng)的啟動(dòng)時(shí)間大大加快,然后優(yōu)化Linux自身的一些啟動(dòng)瓶頸
Linux的啟動(dòng)參數(shù)優(yōu)化:加上了 lpj=99072,節(jié)約了幾十個(gè)毫秒,加上quiet,節(jié)約了大約1秒時(shí)間
修改內(nèi)核編譯選項(xiàng),把不需要的內(nèi)核模塊干掉
最后,Linux自身的啟動(dòng)速度約為1.1秒,整個(gè)系統(tǒng)的啟動(dòng)速度大約4秒多一點(diǎn),初步達(dá)到了優(yōu)化目標(biāo),系統(tǒng)的主要延時(shí)發(fā)生在U-boot 1.3.4的FLASH讀取上,F(xiàn)LASH讀取速度大約只有600KB/S。嘗試把Uboot 2011.12的FLASH驅(qū)動(dòng)移植到U-boot 1.3.4上,花費(fèi)了幾天時(shí)間,終于可以編譯成功了,可惜經(jīng)常出一些莫名其妙的錯(cuò)誤,太不穩(wěn)定,只好放棄。
以我的能力,U-boot優(yōu)化到這里就到頭了,正準(zhǔn)備結(jié)束工作時(shí),發(fā)現(xiàn)了另外一條可以加速系統(tǒng)啟動(dòng)的方法
可以繼續(xù)嘗試,讓我最終把系統(tǒng)的啟動(dòng)時(shí)間減少到了1.7秒。
請(qǐng)看下篇嵌入式Linux啟動(dòng)優(yōu)化手記2 U-boot優(yōu)化
?
評(píng)論