原文:http://blog.sina.com.cn/s/blog_4b687eac01009w39.html
之前由于快毕业等种种原因,一度暂停了linux源代码的阅读,然后中途每次想接着看又发现前面看的知识都有些忘了,又得从新简单的复习一下,于是导致进度以蜗速前进...现在终于是有时间了,好歹把第一个程序bootsect.s----磁盘引导块程序看完了,小小260行的汇编代码,却着实费了我不少功夫,中间也遇上了很多问题,通过上BBS查询,在些做出一些总结,也算是我对我看完的第一个OS源代码的学习报告吧.
这里大部分的回答都是从oldlinux论坛上看来的,只是论坛里排版很乱,看得很不爽,所以自己整理下,并加上我自己的疑问,根据行号进行排序,以便日后更好地学习和查看.
L25 .globl begtext,begdata,begbbs,endtext,enddata,endbbs
L26 .text
L27 begintext:
L28 .data
L29 begdata:
L30 .bss
L31 begbbs:
L32 .text
... ...
L255 .text
L256 endtext:
L257 .data
L258 enddata:
L259 .bss
L260 endbbs:
Q1:可以看出.text .data .bss重叠的,我的疑问是在模块连接时,.text 和其他模块的.text合并,.data和其他模块的.data合并,那不会出现问题?还有就是语法问题,为什么.text会出现3次?
A1: 重叠段的用法通常只用于单个文件。前后两个.text开始text段并起定义段“标号”的用途,这样在带符号调试时可以分清。bootsect和 setup都单独编译链接,不与其他目标文件合并。在组建内核Image文件时,tools/build.c文件会直接把他们去除各自的头结构后顺序组合在一起。.text就定义其后面的代码在.text段中。若后面又出现其他.XXXX,则表示.XXXX后面开始是XXXXX段中的代码或数据。
Q2:我不理解这种分段的方法,难道linus为了把代码段和数据段编译在一个段内?即便如此,为什么最后还要有endtext等标示符?
A2: bootsect程序都统统放在512字节之内。在这么小的地方没有必要分离各个sections。标号begtext和endtext可分别用来指明text段的开始和结束。
Q3:我看了一下MASM的语法,然后又看了一下as86的帮助,但是不知道上面几句话到底起什么作用,在MASM中有定义段的语句,正好对应段寄存器,as86的帮助上说.text,.data是置当前段(set current segment)我想跟MASM中的某些语句相当,但是.text和.bss对应什么段寄存器呢?另外as86中好像没有看到像MASM那样在源代码中分段,是不是as86在汇编的时候自动根据源代码来划分相应段?
A3:这些伪指令是供编译器使用的。这些标号供ld86使用。 .text指明程序中的代码段;.data是数据段;.bss是未初始化的数据区。注意,上面的“段”的含义并不是段寄存器的意思,它只是指明目标文件或执行文件中的代码或数据区(块)。其中对于硬盘上的执行文件来讲不含bss,只有到了执行文件被加载到内存中时才会为其分配bss段(区),并且位于data段的后面。链接程序(例如ld)会使用这些伪指令把所有链接模块中的这些段都分别组合在一起,从而在输出文件中形成已经组合过的代码和数据部分。
Q4: 在启动的文件中定义的几个globl变量没有使用是不是可以不要啊我在阅读过程中看不出这几个globl变量有什么用啊
A4: 一般情况下或者需要使用你自己指定的全局变量时不能不要.global。但是当你的所有段都是重叠的并且不使用除了链接器使用的几个全局变量时,就可以省略它们。例如,你可以把bootsect.s和setup.s中的.global定义都去掉。
Q5:下面代码和上面代码的效果等价么? .globl begtext,begdata,begbbs,endtext,enddata,endbbs .text begintext: begdata: begbbs: ... ..... ....... endtext: enddata: endbbs:
A: 你还需要在.text后给出.data、.bss
L43 ROOT_DEV=0X306 !指定文件系统设备是第2个硬盘的第一个分区。
Q1:如果内核程序和文件系统安装在同一个硬盘里时,是否需要把ROOT_DEV=0X301 /dev/hda1?换成第一个硬盘的第一个分区. 还有程序中load setup、取每个磁道扇区数等时,也需要把驱动器号改成读硬盘标识?
A1: 1. 如果要安装在同一个分区中,那么就需要使用专用独立的能从文件系统中取得内核Image文件并加载的引导软件。例如,shoelace,grub,lilo等。本站有人已经自己做了一个专门的。请参考第2个分论坛中(精华区)。 2. 你说的没错。很多(基本上全部都没用,可删除。不过从这点可以看出Linus当时也在不断学习其他人的东东 L43 ROOT_DEV = 0x306 ! 0x306-/dev/hd6-第2个硬盘的第1个分区
Q2:为什么ROOT_DEV要指向第2个硬盘的第1个分区,而不是第一个硬盘或者其他分区?有什么特别的道理吗?
A2:因为当时Linus的机器上有两个硬盘,而他使用第2个硬盘来开发Linux系统,第1个硬盘上安装的是MINIX系统。
L51 mov cx #256
L52 sub si si
L53 sub di di
L54 rep movw
Q1:将bootsect.s的模块移到0x90200处,请问怎么知道bootsect.s汇编好后是256个字?
A1: 在bootsect.s最后有: 249L .org 508 表示249行后面的代码从508个字节处开始,再往下: .word ROOT_DEV .word 0xAA55 共4个字节,所以整个文件大小就是508+4=512个字节。 Linus在这里用了.org也可以采用填充0的方法,不论怎样,总要使512字节的最后两个字节 是55 AA(用word表是就是AA55了)
L56 jmpi go,INITSEG
L57 go: mov ax,cs
Q1:jmpi的语法是: jmpi 段值,段内偏移.
在汇编里好象标号指的是段内偏移,所以此处的标号go应该是一个段内偏移,感觉是不是有点问题啊?
A1:“jmpi的语法是jmpi 段值,段内偏移”--没错,这是as86的语法。
L77 j load_setup
Q1:是否应为: jmp load_Setup?
A1:不是印刷错误,LD86中就有j这条指令,等价于JMP
L81 <注释> 利用INT 0x13的第8号子功能读取磁盘驱动器参数,入口参数ah=0x08,dl=驱动器号(如果是硬盘则要置位7为1)
Q1: 驱动器号是谁分配的,又是怎么分配的,为啥硬盘要置位7为1?
A1: 是IBM PC机BIOS就这样规定设计的。
L87 seg cs !表示下一条语句的操作数在cs段寄存器所指的段中
L88 mov sectors,cx
Q1:在bootsect.s中定义了sectors, root_dev, sread,head,track等标志符,但是在操作数中含有sectors, root_dev时,需要seg cs, 而操作数含有sread,head,track时,之前却不用加seg cs,why? A1:你仔细看一下会发现,其实所有"seg cs"指令都无用。因为这个程序被连接在一个段中,即代码、数据段都在一起。Linus当时开发时所用的读盘程序可能是单独编写的,或者是从网上下载后修改的。
Q2:语句的意思很好理解,但是这时DS与CS段是一样的,是否这里不加87行语句也是可以的呢?
A2: seg cs可以不用。
L94 mov ah,0x03
L95 xor bh,bh
L96 int ox10
Q1:第94到96行的语句,是否是显示串前必须做的呢?我以前在核心的代码里加调试语句时是没有先读光标位置的。是否是第一次显示时读一次就可以了
A1: 94-96是必须的。用于设置int 0x10调用功能0x13使用的行列值(dx中,串开始位置).若调用串显示函数时al的最低比特位=1(al=1或3),则光标会在显示串后被设置在串的结尾处。否则若al=0或2,则在显示后光标位置不动
L99 mov bx,#0x0007 !page 0,attribute 7 (normal)
Q1:bl=0x07,有什么作用?
A1:这是显示参数,请参考VGA显示卡资料。
L147 sread: .word 1 + SETUPLEN
L148 head: .word 0
L149 track: .word 0
Q1:这里的.word是什么意思?
A1:.word以及其它地方的.byte,.ascii,.org,.test等都是伪指令,.word指的是定义一个字,即定义两个字节,.byte即定义一个字节,依此类推
L153 test ax,#0x0fff
Q1:(1)#0x0fff为啥不是#0x1000呢? (2)这一步是不是多余呀,因为ax肯定是0x1000,为什么还要测试?出了什么错才会导致ax和es不等于0x1000?
A1: (1)TEST指令:两操作数相与不保存结果,只保存特征条件码(影响Flag标志)。这句测试所有三个0xf代表的12个比特位应该都为零。若都为0则段值肯定是0xN000,对应段的地址肯定是0xN0000,也即位于64KB边界上。 (2)因为这是个子程序,它有自己的接口要求。加上这种判断是良好的编程习惯
L162 seg cs
L163 mov ax,sectors
L164 sub ax,sread
... ...
L241 sectors:
L242 .word 0
Q1:在代码中,sectors即每磁道的扇区数被定义为0,而这里又把ax-sread,那所得结果存在ax中的岂不是为负数?
A1:请参见L88 mov sectors,cx.Linus只是在最初的时候将其初始化为0而已.
L170 xor ax,ax
L171 sub ax,bx
L172 shr ax,#9
Q1:170行的代码有疑问,程序跳转到read_it子程序里时,要完成把磁盘里的数据读入到内存中0x10000地址开始的区域中,整个读的过程是先检测本磁道中剩下的扇区所包含的字节总数是不是可以完全放到当前的64K内存段里,如果当前的64K内存段里放不下,就会执行到170行这个地方,170 行来了句 xor ax,ax sub ax,bx shr ax,#9 这几行代码是想获得可以读到当前的64K内存段的扇区个数,存放在al里对这个地方,我有一点不解,如果是第一次读,bx就应该是0了,那ax也是0,结果最后al里面也是0了,那下面紧接着就调用INT 13号中断读磁盘,al 在13号中断里的含义是从磁盘读出的扇区的个数,当al=0时,不知道这个中断是怎么执行的??我查了手册,手册上也没讲INT 13号中断的2号子功能调用,在当al=0时调用的结果是什么,手册上只是说了,al的值在1-128之间是有效的,我就不知道上面讲的那种情况INT 13号中断是怎么处理的了?
A1:第1次读时bx确实是0,但ax不是0。因为sread最初是1+SETUPLEN,即5。因此(80 - 5)*512 = 38400 = 37.5KB 没有超过64KB,所以第1次不会执行170行开始的代码。由于16位寄存器最大只能表示64KB -1,因此这里0表示最大值64KB(已进位)。软盘上一个磁道上有80个扇区,硬盘上一个磁道上最多有63个扇区,这样的话,一个磁道上的字节总数是肯定小于64K的,也就是说不会出现,第一次读磁盘就执行到 170行处的: xor ax,ax sub ax,bx shr ax,#9
Q2:代码161-172行中是计算和验证当前磁道需要读取的扇区数,根据代码可以看出程序将本磁道剩余的全部扇区读到内存中去(在不超过64k的前提下),这里读的是systerm模块,是不是本磁道剩下的扇区正好写完systerm模块,难道没有剩余扇区,或者是扇区不够,将systerm模块一部分写入另外一个磁道?
A2:BOOTSECT.S 读入内存的SYSTEM模块一定是64K的整数倍(参看157-159行)拿1.44MB的软盘来说,一共有2个磁头,80个磁道,每个磁道18个扇区,64K=128个扇区/18是可能出现最后有磁道没有读完的,但是SYSTEM模块不会恰好就是64K的整数倍啊.LINUX在SYSTEM后面加入空洞凑够64K整数倍,只要这个空洞大于1个磁道的扇区数,就能保证整个SYSTEM模块都读入内存中了(最后一个磁道没读完也没关系,因为是空洞!)
L180 mov ax,#1
L181 sub ax,head
L182 jne ok4_read
L183 inc track
Q1:当0磁头的当前道没有数据可读后则去读1磁头上的数据,想问这种情况是适用于哪种软驱的?我想过去是个双面软驱。如果不是双面软驱,则在read_track里则会死循环吧。
A1: PC机使用的软盘驱动器都是双头(双面)的。没有见过哪种PC机用的的是单面的
Q1:在加载system模块时,为什么先把它加载到0x10000处,后来又移到0x00000处,为什么不把它直接加载到0x00000,这时0x00000并没有作为他用啊!
A1:为什么不把系统模块直接加载到物理地址0x0000开始处而要在setup程序中再进行移动呢?这是因
为在setup 程序代码开始部分还需要利用ROM BIOS中的中断调用来获取机器的一些参数(例如显示卡
模式、硬盘参数表等) 。当 BIOS 初始化时会在物理内存开始处放置一个大小为 0x400 字节(1Kb)的中断
向量表,因此需要在使用完BIOS的中断调用后才能将这个区域覆盖掉。
由bootsect.s将system放入内存0x10000处,由setup.s将system移至内存的起始地址.
Q2:请问system模块在磁盘中放在哪里?
A2:紧挨着setup模块,可以从sread=1+4=5中看出
PS:在写完这篇之后,我把bootsect.s的源代码又看了一遍,收获不少~真是需要一读再读啊...特别是对我这种笨小孩...本文的疑问总结就这么多了,其实自己在看的过程中还遇到许多问题,但感觉都很傻,比较简单,读到后面自己也能搞明白,所以没有一一列举,欢迎本文的读者提出新的问题一起讨论,也许本文还有待补充...