📄 1.html
字号:
<center><A HREF="#Content">[目录]</A></center><hr><br><A NAME="I360" ID="I360"></A><center><b><font size=+2>内核解压</font></b></center><br>概述<br>----<p>1) Linux的初始内核映象以gzip压缩文件的格式存放在zImage或bzImage之中, 内核的自举代码将它解压到1M内存开始处. 在内核初始化时, 如果加载了压缩的initrd映象, 内核会将它解压到内存盘中, 这两处解压过程都使用了lib/inflate.c文件.<p>2) inflate.c是从gzip源程序中分离出来的, 包含了一些对全局数据的直接引用, 在使用时需要直接嵌入到代码中. gzip压缩文件时总是在前32K字节的范围内寻找重复的字符串进行编码, 在解压时需要一个至少为32K字节的解压缓冲区, 它定义为window[WSIZE].inflate.c使用get_byte()读取输入文件, 它被定义成宏来提高效率. 输入缓冲区指针必须定义为inptr, inflate.c中对之有减量操作. inflate.c调用flush_window()来输出window缓冲区中的解压出的字节串, 每次输出长度用outcnt变量表示. 在flush_window()中, 还必须对输出字节串计算CRC并且刷新crc变量. 在调用gunzip()开始解压之前, 调用makecrc()初始化CRC计算表. 最后gunzip()返回0表示解压成功.<p><br>3) zImage或bzImage由16位引导代码和32位内核自解压映象两个部分组成. 对于zImage, 内核自解压映象被加载到物理地址0x1000, 内核被解压到1M的部位. 对于bzImage, 内核自解压映象被加载到1M开始的地方, 内核被解压为两个片段, 一个起始于物理地址0x2000-0x90000,另一个起始于高端解压映象之后, 离1M开始处不小于低端片段最大长度的区域. 解压完成后,这两个片段被合并到1M的起始位置.<p><br>解压根内存盘映象文件的代码<br>--------------------------<p>; drivers/block/rd.c<br>#ifdef BUILD_CRAMDISK<p>/*<br> * gzip declarations<br> */<p>#define OF(args) args ; 用于函数原型声明的宏<br>#ifndef memzero<br>#define memzero(s, n) memset ((s), 0, (n))<br>#endif<br>typedef unsigned char uch; 定义inflate.c所使用的3种数据类型<br>typedef unsigned short ush;<br>typedef unsigned long ulg;<br>#define INBUFSIZ 4096 用户输入缓冲区尺寸<br>#define WSIZE 0x8000 /* window size--must be a power of two, and */<br> /* at least 32K for zip's deflate method */<p>static uch *inbuf; 用户输入缓冲区,与inflate.c无关<br>static uch *window; 解压窗口<br>static unsigned insize; /* valid bytes in inbuf */<br>static unsigned inptr; /* index of next byte to be processed in inbuf */<br>static unsigned outcnt; /* bytes in output buffer */<br>static int exit_code;<br>static long bytes_out; 总解压输出长度,与inflate.c无关<br>static struct file *crd_infp, *crd_outfp;<p>#define get_byte() (inptr<br>/* Diagnostic functions (stubbed out) */ 一些调试宏<br>#define Assert(cond,msg)<br>#define Trace(x)<br>#define Tracev(x)<br>#define Tracevv(x)<br>#define Tracec(c,x)<br>#define Tracecv(c,x)<p>#define STATIC static<p>static int fill_inbuf(void);<br>static void flush_window(void);<br>static void *malloc(int size);<br>static void free(void *where);<br>static void error(char *m);<br>static void gzip_mark(void **);<br>static void gzip_release(void **);<p>#include "../../lib/inflate.c"<p>static void __init *malloc(int size)<br>{<br> return kmalloc(size, GFP_KERNEL);<br>}<p>static void __init free(void *where)<br>{<br> kfree(where);<br>}<p>static void __init gzip_mark(void **ptr)<br>{<br> ; 读取用户一个标记<br>}<p>static void __init gzip_release(void **ptr)<br>{<br> ; 归还用户标记<br>}<p>/* ===========================================================================<br> * Fill the input buffer. This is called only when the buffer is empty<br> * and at least one byte is really needed.<br> */<p>static int __init fill_inbuf(void) 填充输入缓冲区<br>{<br> if (exit_code) return -1;<br> insize = crd_infp->f_op->read(crd_infp, inbuf, INBUFSIZ,<br> if (insize == 0) return -1;<br> inptr = 1;<br> return inbuf[0];<br>}<p>/* ===========================================================================<br> * Write the output window window[0..outcnt-1] and update crc and bytes_out.<br> * (Used for the decompressed data only.)<br> */<p>static void __init flush_window(void) 输出window缓冲区中outcnt个字节串<br>{<br> ulg c = crc; /* temporary variable */<br> unsigned n;<br> uch *in, ch;<p> crd_outfp->f_op->write(crd_outfp, window, outcnt,<br> in = window;<br> for (n = 0; n ch = *in++;<br> c = crc_32_tab[((int)c ^ ch) 0xff] ^ (c >> 8); 计算输出串的CRC<br> }<br> crc = c;<br> bytes_out += (ulg)outcnt; 刷新总字节数<br> outcnt = 0;<br>}<p>static void __init error(char *x) 解压出错调用的函数<br>{<br> printk(KERN_ERR "%s", x);<br> exit_code = 1;<br>}<p><br>static int __init<br>crd_load(struct file * fp, struct file *outfp)<br>{<br> int result;<p> insize = 0; /* valid bytes in inbuf */<br> inptr = 0; /* index of next byte to be processed in inbuf */<br> outcnt = 0; /* bytes in output buffer */<br> exit_code = 0;<br> bytes_out = 0;<br> crc = (ulg)0xffffffffL; /* shift register contents */<p> crd_infp = fp;<br> crd_outfp = outfp;<br> inbuf = kmalloc(INBUFSIZ, GFP_KERNEL);<br> if (inbuf == 0) {<br> printk(KERN_ERR "RAMDISK: Couldn't allocate gzip buffer\n");<br> return -1;<br> }<br> window = kmalloc(WSIZE, GFP_KERNEL);<br> if (window == 0) {<br> printk(KERN_ERR "RAMDISK: Couldn't allocate gzip window\n");<br> kfree(inbuf);<br> return -1;<br> }<br> makecrc();<br> result = gunzip();<br> kfree(inbuf);<br> kfree(window);<br> return result;<br>}<p>#endif /* BUILD_CRAMDISK */<p>32位内核自解压代码<br>------------------<p>; arch/i386/boot/compressed/head.S<p>.text<br>#include ·<br>#include<br> .globl startup_32 对于zImage该入口地址为0x1000; 对于bzImage为0x101000<br>startup_32:<br> cld<br> cli<br> movl $(__KERNEL_DS),%eax<br> movl %eax,%ds<br> movl %eax,%es<br> movl %eax,%fs<br> movl %eax,%gs<p> lss SYMBOL_NAME(stack_start),%esp # 自解压代码的堆栈为misc.c中定义的16K字节的数组<br> xorl %eax,%eax<br>1: incl %eax # check that A20 really IS enabled<br> movl %eax,0x000000 # loop forever if it isn't<br> cmpl %eax,0x100000<br> je 1b<p>/*<br> * Initialize eflags. Some BIOS's leave bits like NT set. This would<br> * confuse the debugger if this code is traced.<br> * XXX - best to initialize before switching to protected mode.<br> */<br> pushl $0<br> popfl<br>/*<br> * Clear BSS 清除解压程序的BSS段<br> */<br> xorl %eax,%eax<br> movl $ SYMBOL_NAME(_edata),%edi<br> movl $ SYMBOL_NAME(_end),%ecx<br> subl %edi,%ecx<br> cld<br> rep<br> stosb<br>/*<br> * Do the decompression, and jump to the new kernel..<br> */<br> subl $16,%esp # place for structure on the stack<br> movl %esp,%eax<br> pushl %esi # real mode pointer as second arg<br> pushl %eax # address of structure as first arg<br> call SYMBOL_NAME(decompress_kernel)<br> orl %eax,%eax # 如果返回非零,则表示为内核解压为低端和高端的两个片断<br> jnz 3f<br> popl %esi # discard address<br> popl %esi # real mode pointer<br> xorl %ebx,%ebx<br> ljmp $(__KERNEL_CS), $0x100000 # 运行start_kernel<p>/*<br> * We come here, if we were loaded high.<br> * We need to move the move-in-place routine down to 0x1000<br> * and then start it with the buffer addresses in registers,<br> * which we got from the stack.<br> */<br>3:<br> movl $move_routine_start,%esi<br> movl $0x1000,%edi<br> movl $move_routine_end,%ecx<br> subl %esi,%ecx<br> addl $3,%ecx<br> shrl $2,%ecx # 按字取整<br> cld<br> rep<br> movsl # 将内核片断合并代码复制到0x1000区域, 内核的片段起始为0x2000<p> popl %esi # discard the address<br> popl %ebx # real mode pointer<br> popl %esi # low_buffer_start 内核低端片段的起始地址<br> popl %ecx # lcount 内核低端片段的字节数量<br> popl %edx # high_buffer_start 内核高端片段的起始地址<br> popl %eax # hcount 内核高端片段的字节数量<br> movl $0x100000,%edi 内核合并的起始地址<br> cli # make sure we don't get interrupted<br> ljmp $(__KERNEL_CS), $0x1000 # and jump to the move routine<p>/*<br> * Routine (template) for moving the decompressed kernel in place,<br> * if we were high loaded. This _must_ PIC-code !<br> */<br>move_routine_start:<br> movl %ecx,%ebp<br> shrl $2,%ecx<br> rep<br> movsl # 按字拷贝第1个片段<br> movl %ebp,%ecx<br> andl $3,%ecx<br> rep<br> movsb # 传送不完全字<br> movl %edx,%esi<br> movl %eax,%ecx # NOTE: rep movsb won't move if %ecx == 0<br> addl $3,%ecx<br> shrl $2,%ecx # 按字对齐<br> &nbs
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -