linux0.11-----从启动程序开始
2013-11-09 13:14 islandscape 阅读(787) 评论(1) 编辑 收藏 举报今天终于鼓起勇气在博客园开博,博客是个分享经验、交流知识的地方,最近在研究linux0.11内核,想把自己的所学记录下来,以备后用。欢迎大家留言批评指正或联系 islandscape@163.com,也欢迎大家一起学习、一起进步。
当我们按下计算机电源键后发生了什么呢?主板接收到一个启动信号,启动供电系统后BIOS进行自检,若发现某个部件不能正常工作系统将进入死循环。所有硬件都通过自检后,系统将控制权交给BIOS。BIOS按照配置顺序读驱动器,将驱动器的第一个扇区(512B)加载到物理地址0x7c00地址,找到启动扇区后从0x7c00这个地址执行启动程序。为什么BIOS会把第一个扇区加载到0x7c00?这是早期设计者们约定的。
下面看一段启动程序,这段程序开机后会显示“hello world”字样。
mov ax,0x7c0 mov ds,ax mov es,ax jmp go go: mov cx,11 mov dx,0x1004 mov bx,0x000c mov bp,msg mov ax,0x1301 int 0x10 loop: jmp loop msg: DB "hello world" times 510-($-$$) db 0 boot_flag: DW 0xAA55
mov ax,0x7c0,将寄存器ax赋值为立即数0x7c0。之后将段寄存器ds、es赋值为0x7c0。不直接赋值ds、es为0x7c0的原因是所有段寄存器(cs、ds、es、ss)都是对用户不可见的,不能直接对段寄存器操作,只能通过通用寄存器间接赋值。
为什么是把0x7c0赋值给段寄存器而不是别的值?最早的处理器8086 CPU数据总线是16位的(CPU寄存器是16位的),而地址总线是20位的。16位的寄存器只能寻址到64k(1^16B)远远小于20位地址线能够表示的寻址范围(1--1^20B(1M))。采用单个寄存器寻址总会有4位地址线是浪费的,于是大牛们想出了一种方法:由两个寄存器组成一个寻址单元,一个寄存器的值左移四位(这就变成20位了)再加上另一个寄存器的值就得到了真正寻址地址。这样很像把1M物理地址分成了2^4=16个段,每个段大小是2^16B=64k。偏移四位的寄存器叫做段寄存器,段寄存器有CS、DS、SS、ES、FS、GS六个,另一个寄存器表示段内偏移,一般用通用寄存器表示。这就是大名鼎鼎的实模式寻址,也被写做seg:off,seg为段寄存器、off是段内偏移。常见的寄存器寻址组合有cs:ip 、ss:sp、es:di、ds:bx、ds:ax、ds:cx等。计算机上电后进入实模式,采用上述寻址方式,前面提到bios将该程序加载到0x7c00地址并执行。0x7c0<<4 = 0x7c00,这正是将段寄存器设为0x7c0的原因。
jmp指令是段内转跳指令,段内转跳的意思是跳到该段的另一个偏移地址。对应的段间转跳(jmpi seg,off)是跳到不同段的不同偏移地址执行。jmp go表示执行标记为go的代码。go在汇编中是一个标识,它会被编译器翻译成一个偏移地址0xyyyy,而jmp go运行时为jmp 0xyyyy。有了这个标识,编译器会帮我们算好这个偏移地址,我们可以把心思放在程序上面。
从go标识到loop这段代码把“hello world”采用BIOS 0x10号中断输出到屏幕上,之后程序陷入死循环(jmp loop)。
BIOS中断在实模式中很重要,很多操作如读磁盘、屏幕输出都是通过它完成的。0x10号中断使用方法如下:
- ------------------------------------------------------------------
- INT 0x10功能0x13
- ------------------------------------------------------------------
- 描述:
- 以电传打字机的方式显示字符串
- 接受参数:
- AH 0x13
- AL 显示模式
- BH 视频页
- BL 属性值(如果AL=0x00或0x01)
- CX 字符串的长度
- DH,DL 屏幕上显示起始位置的行、列值
- ES:BP 字符串的段:偏移地址
- 返回值:
- 无
- 显示模式(AL):
- 0x00:字符串只包含字符码,显示之后不更新光标位置,属性值在BL中
- 0x01:字符串只包含字符码,显示之后更新光标位置,属性值在BL中
- 0x02:字符串包含字符码及属性值,显示之后不更新光标位置
- 0x03:字符串包含字符码及属性值,显示之后更新光标位置
- -------------------------------------------------------------------
$意为当前地址、$$是程序起始地址,($-$$)表示程序的长度,times 510-($-$$) db 0将程序末尾到地址510全部填充0。DW 0xAA55将程序的511、512字节填充为0xAA55。为什么要这样做? BIOS依次将驱动器上磁盘第一个扇区(512B)加载到地址0x7c00,若该扇区最后两个字节为0xAA55,则被认为是启动扇区,如果不是则继续寻找下一个驱动器。故谢煜波将引导程序特点归结为三点:1、必须有不多不少512个字节。2、必须放在第一个扇区。3、最后两字节必须为0xAA55。
参考资料:
0x10号BIOS中断使用方法摘自:http://blog.csdn.net/guzhou_diaoke/article/details /8397658
谢煜波,《操作系统引导探究》
赵炯, 《linux内核完全注释》