Linux0.11第五回进入保护模式前的最后一次折腾内存
本系列将以一种新奇的阅读心态阅读和欣赏Linux 0.11的所有核心代码,从启动后的代码执行序列中了解操作系统的技术细节和设计思路。
你会跟着我,看着一个操作系统从无到有,最后一步一步实现它复杂而精致的设计看完这个系列,希望你能感叹原来操作系统源代码就是这个蠢东西
以下是已发表文章的列表想详细了解这个系列,可以从开篇的话开始
开场白
前两行代码
给自己挪个位置。
做好最基本的准备工作。
将硬盘的其他部分放入内存。
这个系列的GitHub地址如下:
—正文开始—
上一次,上一次,我们说操作系统已经完成了从硬盘到内存的各种加载,从内存到内存的复制。
至此,bootsect.s的整个使命已经完成,也是我们看完的第一个操作系统源文件然后跳转到0x90200,这里的代码在安装程序的开始处
start:movax,# 0x9000thisisdoneinbootsectalready,butmovds,axmovah,# 0x03readcursorposxorbh,BH,saveitinknownplace,con_initfetchesmov,dx,itfrom0x90000。
还有一个int指令。
如果你仔细阅读前面的文章,你就能猜到它要做什么还记得有一个int 0x13触发了BIOS提供的磁盘读取中断吗这个int 0x10也是如此,同样触发BIOS提供的显示服务中断处理程序,ah寄存器赋给0x03,表示显示服务中具体的光标位置读取功能
具体来说,BIOS提供了哪些中断服务,如何调用并获取返回值请自行寻找资料,这里只说结果
当int 0x10中断程序完成并返回时,dx寄存器中的值指示光标的位置具体地说,高八位dh存储行号,低八位dl存储列号
这里说明一下:电脑开机自检后会自动初始化为文本模式在这种模式下,屏幕可以显示25行,每行80个字符,即80列
下一个mov,dx将在这个内存地址存储这个光标位置注意,我们前面说过,这个内存地址只是一个偏移地址,需要加上ds寄存器中存储的段基址最终的内存地址是0x90000,其中存储了光标位置,供以后初始化控制台时使用
所以从这里可以看出,这和平时调用一个方法没什么区别,只不过这里寄存器的用法相当于参数和返回值,这里的0x10中断号相当于方法名。
这里应该是之前说过的一句话操作系统内核初期,到处都是BIOS调试器如果有现成的,就用吧
接下来几行代码的逻辑和刚才一样调用BIOS中断来获取一些信息,然后将其存储在内存中的某个位置让我们快速浏览一下
例如获得存储器信息Getmemorysemovah,# 0x88int 0x15mov ,ax获取显卡的显示模式Getvideo—carddata:movah,#0x0fint0x10mov,bx,bh=displaypagemov,ax,Al=videomode,ah=windowwidth检查显示模式,获取参数,检查forega/vgaandsomeconfigparametersmovah,# 0x12movbl,# 0x10int 0x10mov ,axmov ,bxmov ,CX得到第一块硬盘的信息Gethd0datamovax,# 0x0000movds,axldssi, movax,# initsegmoves,axmovdi,# 0x0080movcx,# 0x10repmovsb获取第二块硬盘的信息
以上原理都一样。
我们没有必要去思考理解操作系统没多大区别我们只需要知道存储在内存中的信息是什么,在哪里,然后我们就会使用它们
内存长度名称0x900002光标位置0x90022
扩展内存号0x900042显示页面0x900061。
显示模式0x900071字符列数0x900082未知0x9000A1
显示内存0x9000B1。
显示状态0x9000C2显卡特性参数0x9000E1。
屏幕行0x9000F1屏幕列0x9008016
1硬盘参数表0x9009016硬盘2参数表0x901FC2
根号
由于之后很快就会使用C语言进行编程,虽然汇编和C语言也可以以变量的形式传递数据,但这需要编译器在链接时做一些额外的工作,所以双方约定一个内存地址会更方便我把它放在这里,你可以从这里拿走,它就完成了这恐怕是最原始最直观的变量传递方式了
存储了这些信息之后,操作系统会做什么我们继续往下看
cli不允许中断,
只有一行cli,意思是关闭中断。
后来因为要重写原来写在BIOS里的中断向量表,也就是销毁它,自己写中断向量表,这时候就不允许中断了。
继续看。
firstwemovethesystemtoit ' srightfulplacemovax,#0x0000cld'direction'=0,movsmovesforwarddo_move:moves,axdestinationsegmentaddax,#0x1000cmpax,#0x9000jzend_movemovds,axsourcesegmentsubdi,disubsi,simovcx,# 0x8000jmpdo _ move然后weloadthesegmentdescriptorsend _ move:...
你熟悉后面的代表movsw吗最初,我们在将操作系统代码从0x7c00移动到0x90000时使用了这条指令我们来回忆一下
与前面的原理一样,也要进行内存复制操作最后的结果就是从内存地址0x10000到0x90000的所有内容都被复制到内存的初始0位置,大概就是这样的效果
由于之前的各种加载复制,内存看起来很乱又到了一波取舍和排序的时候了现在我们重新安排一下内存布局
栈顶地址仍然是0x9FF00,没有改变。
0x90000向上开始的位置原来是bootsect和setup程序的代码现在,为了记录内存,硬盘,显卡等一些临时存储的数据,bootsect的部分代码已经被操作系统部分覆盖
内存512K的前0到0x80000由系统模块占用如前所述,这个系统模块是链接除bootsect和setup之外的所有程序的结果,可以理解为操作系统的整体
所以现在的内存布局是这样的。
好了,记住上图就行了这次又清楚了吗
接下来要做一些技术工作,就是模式转换,需要从现在的16位实模式转换到下一个32位保护模式这是一个大工程!我认为这也是操作系统源代码之旅的第一个精彩之处
我们身后的世界越来越精彩欲知后事如何,且听下回分解
郑重声明:此文内容为本网站转载企业宣传资讯,目的在于传播更多信息,与本站立场无关。仅供读者参考,并请自行核实相关内容。