日期:2014-05-16  浏览次数:20760 次

linux 简单分析
    为了提高自己,不知不觉踏上了linux源码分析之路了!
    我选择linux源码版本是linux 0.11,为什么选择它?因为它代码量少且资料多。

    针对它的分析是建立于网上资料之上(快捷、效果好)。

    项目如图:


  该项目是网上某某已经编译好了,并且对它进行了分析。笔者只是学习他的皮毛。

  首先进入项目的是bootsect:这个程序是linuxkernel的第一个程序,包括了linux自己的bootstrap程序,但是在 说明这个程序前,必须先说明一般IBMPC开机时的动作(此处的开机是指"打开PC的电源" )。它是第一个被读入内存中并执行的程序,现在,我们可以开始来看看到底它做了什么。 

第一步 :bootsect将它"自己"从被ROMBIOS载入的绝对地址0x7C00处搬到0x90000处, 然后利用一个jmpi(jumpindirectly)的指令,跳到新位置的jmpi的下一行去执行…

表示将跳到CS为0x9000,IP为offset"go"的位置(CS:IP=0x9000:offsetgo),其中I NITSEC=0x9000定义于程序开头的部份,而go这个label则恰好是下一行指令所在的位置。

第二步:接着,将其它segmentregisters包括DS,ES,SS都指向0x9000这个位置,与CS看齐 。

第叁步:接着利用BIOS中断服务int13h的第0号功能,重置磁盘控制器,使得刚才的设定发挥功能。

 第四步:完成重置磁盘控制器之后,bootsect就从磁盘上读入紧邻着bootsect的setup程序, 也就是以后将会介绍的setup.S,此读入动作是利用BIOS中断服务int13h的第2号功能。

第五步:再来,就要读入真正linux的kernel了,也就是你可以在linux的根目录下看到的"v mlinuz"。

第六步:接下来做的事是检查root device,之后就仿照一开始的方法,利用indirect jump跳到刚刚已读入的setup部份。

 ;//bootsec.s文件说明如下:磁盘的引导块程序,驻留在磁盘的第一扇区。
 ;//在PC机加电rom bios自检之后,引导扇区由bios加载到内存0x7c00处,然后将自己移动到内存0x90000处。
 ;//该程序的主要作用是首先将setup模块从磁盘加载到内存中,紧接着bootsect的后面位置(0x90200),然后利用bios中断0x13中断去磁盘参数表中当前引导盘的参数,
 ;//然后在屏幕上显示"Loading system..."字符串。再者将system模块从磁盘上加载到内存0x10000开始的地方。随后确定根文件系统的设备号,如果没有指定,
 ;//则根据所保存的引导盘的每类型和种类,并保存设备号与boot_dev,最后长跳转到 setup程序开始处0x90200执行setup程序。 
 
 .model tiny
 .386p
;// SYSSIZE是要加载的节数(16字节为1节)。3000h共为30000h字节=192kB
;// 对当前的版本空间已足够了。
 SYSSIZE = 3000h		;// 指编译连接后system模块的大小。
						;// 这里给出了一个最大默认值。

 SETUPLEN = 4			;// setup程序的扇区数(setup-sectors)值
 BOOTSEG  = 07c0h		;// bootsect的原始地址(是段地址,以下同)
 INITSEG  = 9000h		;// 将bootsect移到这里
 SETUPSEG = 9020h		;// setup程序从这里开始
 SYSSEG   = 1000h		;// system模块加载到10000(64kB)处.
 ENDSEG   = SYSSEG + SYSSIZE		;// 停止加载的段地址

;// DEF_ROOT_DEV:	000h - 根文件系统设备使用与引导时同样的软驱设备.
;//		301 - 根文件系统设备在第一个硬盘的第一个分区上,等等
ROOT_DEV = 301h;//指定根文件系统设备是第1个硬盘的第1个分区。这是Linux老式的硬盘命名
						;//方式,具体值的含义如下:
						;//设备号 = 主设备号*256 + 次设备号 
						;//          (也即 dev_no = (major<<8 + minor)
						;//(主设备号:1-内存,2-磁盘,3-硬盘,4-ttyx,5-tty,6-并行口,7-非命名管道)
						;//300 - /dev/hd0 - 代表整个第1个硬盘
						;//301 - /dev/hd1 - 第1个盘的第1个分区
						;//... ...
						;//304 - /dev/hd4 - 第1个盘的第4个分区
						;//305 - /dev/hd5 - 代表整个第2个硬盘
						;//306 - /dev/hd6 - 第2个盘的第1个分区
						;//... ...
						;//309 - /dev/hd9 - 第1个盘的第4个分区 

;/* ************************************************************************
;	boot被bios-启动子程序加载至7c00h(31k)处,并将自己移动到了
;	地址90000h(576k)处,并跳转至那里。
;	它然后使用BIOS中断将'setup'直接加载到自己的后面(90200h)(576.5k),
;	并将system加载到地址10000h处。
;
;	注意:目前的内核系统最大长度限制为(8*65536)(512kB)字节,即使是在
;	将来这也应该没有问题的。我想让它保持简单明了。这样512k的最大内核长度应该
;	足够了,尤其是这里没有象minix中一样包含缓冲区高速缓冲。
;
;	加载程序已经做的够简单了,所以持续的读出错将导致死循环。只能手工重启。
;	只要可能,通过一次取取所有的扇区,加载过程可以做的很快的。
;************************************************************************ */
code segment		;// 程序从_main标号开始执行。
	assume cs:code
start:					;// 以下10行作用是将自身(bootsect)从目前段位置07c0h(31k)
						;// 移动到9000h(576k)处,共256字(512字节),然后跳转到
						;// 移动后代码的 go 标号处,也即本程序的下一语句处。 
	mov	ax,BYTE PTR BOOTSEG		;// 将ds段寄存器置为7C0h
	mov	ds,ax
	mov	ax,BYTE PTR INITSEG		;// 将es段寄存器置为9000h
	mov	es,ax
	mov	cx,256			;// 移动计数值 = 256字 = 512 字节
	sub	si,si			;// 源地址   ds:si = 07C0h:0000h
	sub	di,di			;// 目的地址 es:di = 9000h:0000h
	rep movsw			;// 重复执行,直到cx = 0;移动1个字
;	jmp INITSEG:[go] 	;// 间接跳转。这里INITSEG指出跳转到的段地址。
    db 0eah				;// 间接跳转指令码
	dw go
	dw INITSEG
go:	mov	ax,cs			;// 将ds、es和ss都置成移动后代码所在的段处(9000h)。
	mov	ds,ax			;// 由于程序中有堆栈操作(push,pop,call),因此必须设置堆栈。
	mov	es,ax
;// put stack at 9ff00.  将堆栈指针sp指向9ff00h(即9000h:0ff00h)处
	mov	ss,ax
	mov	sp,0FF00h		;/* 由于代码段移动过了,所以要重新设置堆栈段的位置。
						;   sp只要指向远大于512偏移(即地址90200h)处
						;   都可以。因为从90200h地址开始处还要放置setup程序,
						;   而此时setup程序大约为4个扇区,因此sp要指向大
						;   于(200h + 200h*4 + 堆栈大小)