代码改变世界

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号中断使用方法如下:

  1. ------------------------------------------------------------------  
  2.             INT 0x10功能0x13  
  3. ------------------------------------------------------------------
  4. 描述:  
  5.     以电传打字机的方式显示字符串  
  6. 接受参数:  
  7.     AH          0x13  
  8.     AL          显示模式  
  9.     BH          视频页  
  10.     BL          属性值(如果AL=0x00或0x01)  
  11.     CX          字符串的长度  
  12.     DH,DL     屏幕上显示起始位置的行、列值  
  13.     ES:BP     字符串的段:偏移地址  
  14. 返回值:  
  15.     无  
  16. 显示模式(AL):  
  17.     0x00:字符串只包含字符码,显示之后不更新光标位置,属性值在BL中  
  18.     0x01:字符串只包含字符码,显示之后更新光标位置,属性值在BL中  
  19.     0x02:字符串包含字符码及属性值,显示之后不更新光标位置  
  20.     0x03:字符串包含字符码及属性值,显示之后更新光标位置  
  21. ------------------------------------------------------------------- 

  $意为当前地址、$$是程序起始地址,($-$$)表示程序的长度,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内核完全注释》