APP下载

一步步编写操作系统 12 程式码段、资料段、栈和cpu暂存器的关系

消息来源:baojiabao.com 作者: 发布时间:2024-05-19

报价宝综合消息一步步编写操作系统 12 程式码段、资料段、栈和cpu暂存器的关系

先说下什么是暂存器。

暂存器是一种物理储存元件,只不过它是比一般的储存介质要快,能够跟上cpu的步伐,所以在cpu内部有好多这样的暂存器用来给cpu存取资料。

先简短说这一两句,暂时离开一下主题,咱们先看看相对熟悉一些的概念——快取。

快取也是一项非常伟大的发明,成功解决了速度不匹配装置之间的资料传输,并且在一般情况下,IO是整个系统的瓶颈,快取的出现,有效减少了低速IO装置的访问频率,从而大幅度的提升了速度。

大家对快取一定不会陌生,生活中处处都是快取的应用。比如浏览器在请求一个域名的ip时:

1浏览器内部都有dns客户端,它先查询本地dns快取中是否有该域名的ip,如果有就直接去访问该ip。如果没有,该dns客户端先要查询自己主机所设定的dns服务器,然后去该dns服务器去查询ip。如果该dns服务器本地快取中有该域名的A记录(域名与ip地址的对应记录),则直接返回给浏览器中的dns客户端。没有该域名的A记录,就通过递回的方式向上询问其它dns服务器,也许问到了根dns服务器才找到了答案。于是这路上所有被询问过的dns服务器,都将此域名对应的A记录快取到自己的cache中,以备下次再有相同域名查询时好直接返回。浏览器中的dns客户端得到此域名的ip地址后,也将该域名和ip放在自己的快取中,以备下次使用者再键入同一域名时,避免再查一次ip。浏览器开始通过网络用http协议访问该ip地址的80埠(预设是80埠,除非特别指定)。一般情况下该ip对应的装置不是最终的web服务器,很少有人把web服务器直接暴露在公网。假设该ip对应的装置是台网关(一般是硬件路由装置),该闸道器检查本地快取中是否有相关web服务器的快取,若有则直接将该http请求分配给快取中的web服务器。否则从服务器列表中重新分配一台web服务器,将该http请求转发给该web服务器处理。随后将该web服务器的ip地址(一般是内网地址)和埠号快取起来,以备下次该使用者的请求到来时,依然给该web服务器。有的闸道器可以识别使用者cookie资讯,从而将可以请求再次落到上一个请求的web服务器上。web服务器拿到请求后,如果是静态请求,先检查自己的快取中是否有该页面的记录,否则直接从硬盘上取出页面,将其返回后,再存入本地静态快取中。如果动态请求,先交给自己的cgi去处理。cgi拿到请求后,先检查自己的快取系统如memcache,如果快取中没有,与数据库建立连线,向数据库发出请求。数据库也是先检查自己的快取,若没有结果集,则从表中检索到资料后返回,并将结果集快取起来。cgi拿到资料后,返回给web服务器。并将资料快取到memcache中。web服务器拿到了资料后,将资料返回给闸道器。由于是动态资料,不需要快取。闸道器拿到资料后,直接返回给浏览器。如果浏览器发现其中有静态资料,如图片,也将静态资料快取到使用者的internet 临时目录。您看,就这么一个不起眼的网页请求,就经过了12步,几乎每一步都要快取结果,当然每一层的快取都有一定的失效时间,超过多少秒后就将快取中的记录置为无效。这其中还有另外一个术语,如果快取中恰好有需要的内容,这就称为命中hit,否则称为缺失mis。

其实上面的快取,我还没说全呢,硬件之间还有快取呢,比如硬盘控制器和硬盘……得,您还是打住吧,我是来学操作系统的,您跟我说这干吗,还说了12个步骤,这与我何干?

兄弟稍安误燥,待小弟细细道来。前面所说的各种快取,是为了引出cpu中的快取。cpu中的一级快取L1,二级快取L2,它们都是SRAM,即静态随机访问储存器,它是最快的储存器啦。也许您又说:“是啊,这个我知道,那又怎么样?”

好啦不卖关子啦,也许您只听说L1、L2、SRAM,但您可能不知道的是,SRAM是用暂存器来储存资料的,这就是SRAM快的原因。而暂存器为什么快呢?原因是,暂存器是使用触发器实现的,这也是一种储存电路,工作速度极快,是纳秒级别的,您想暂存器能不快吗,这和cpu的速度是一个级别啦。硬件我也不是很了解,所以只能跟您说到这了,点到为止,对于硬件背后的原理,咱们蜻蜓点水即可。

巧妇难为无米之炊,不管是指令还是资料,cpu内部总该有个地方存放它们,否则cpu这位巧妇连锅都没有,还怎么给大家烧一桌好菜呢。所以,暂存器是给cpu处理资料的场所。

到这里,大家心里应该对暂存器有个感性认识了,这样学起来,似乎看得见摸得着了。

cpu中的暂存器大致上分为两大类:

λ一类是其内部使用的,对程序员不可见。“是否可见”不是说暂存器是否能看得见,是指程序员是否能使用。cpu内部有其自己的执行机制,是按照某个预定框架进行的,为了cpu能够执行下去,必然会有一些暂存器来做资料的支撑,给cpu内部的资料提供储存空间。这一部分对外是不可见的,我们无法使用它们。比如全域性描述符表暂存器GDTR,中断描述符表暂存器IDTR,区域性描述符表暂存器LDTR,任务暂存器TR,控制暂存器CR0~3,指令指标暂存器IP,标志暂存器flags,除错暂存器DR0~7。λ另一类是对程序员可见的暂存器。我们进行组合语言程式设计时,能够直接操作的就是这些暂存器。如段暂存器,通用暂存器。虽说第一类的程式是不可见暂存器,我们没办法直接使用,但它们中的一部分还得由咱们给初始化呢。比如全域性描述符表暂存器GDTR,以后咱们还要通过lgdt指令为其指定全域性描述符表的地址及偏移量。对于中断描述符表暂存器IDTR,咱们也是要通过lidt指令为其指定中断描述符表的地址。而区域性描述符表暂存器LDTR,可以用lldt指令为其指定区域性描述符表ldt(但我们效仿了现代操作系统,未用区域性描述符表ldt)。对于任务暂存器TR,我们也要用ltr指令为其在指定一个任务状态段tss。对于flags暂存器,我们也有办法设定它,系统提供了pushf和popf指令,分别用于将flags暂存器的内容压入栈,将栈中内容弹到flags暂存器。额外说一句,ldt和tss都位于gdt中。这些方面的内容咱们在学习保护模式后都会讲到。

在真实模式下,预设用到的暂存器都是16位宽,咱们说说具体的暂存器吧。段暂存器是做啥的呢。说到段暂存器存在的理由,得先说到内存访问机制。cpu是用“段基址:段内偏移地址”的形式来访问内存的,如果您对此访存形式不了解,前面第0章中有解释过分段,而且在下一节中也是讲内存分段的理由。您合理安排阅读。

现在假设您已经了解分段机制啦,上面提到的“段基址:段内偏移地址”中的段基址,就是用段暂存器来储存的,段暂存器的作用就是指定一片内存的起始地址,故也称为段基址暂存器。尽管段基址在真实模式下要乘以16,在保护模式下只是个选择子(保护模式中会讲),但其作用就是指定一片内存的起始地址。而段内偏移地址,顾名思义,仅仅相对于此起始地址的偏移量。

访问内存,是要通过地址总线,给地址总线一个数字(也就是地址),地址总线就能找到以该数字为地址的内存。可是这个数字是哪来的呢?对于首次访问内存之前,其内存地址肯定是要放在与内存不同的储存介质中更合适也更容易。如果用内存来储存内存地址,首先访问该内存就是个问题,该内存的地址是什么?这就跟先有鸡还是先有蛋的本源问题一样了。您可能会有疑问,地址也只是个数字,把数字存放在内存中有什么不行的。我这里所说的并不是内存定址,您说的这个数字只是该内存单元中的内容,这是内存定址,前提是您已经给地址总线提交了储存该数字的内存地址。我说的是,提交给地址总线的地址,是从哪里获得的。不知道我说清楚了没有,再举个例子,我想用木材做一个船模,我不可能还用木质工具去加工它,只能用铁器等比木更硬的材料通过削、磨等方式将它加工出来。换在计算机中也一样,访问内存就要提供地址,初次访问内存时,该地址要么用立即数,要么储存在某个储存器中能让cpu取出来再访问内存,肯定不能用内存本身来存。由于暂存器比内存更高阶,cpu更能接受,所以就用暂存器来储存内存地址。由于要指定的是内存中的一段区域的起始地址,所以称之为段基址暂存器,也称段暂存器,无论是在真实模式下还是保护模式下,它们都是16位宽。

下面就是真实模式下的段暂存器

程式码段简而言之就是把所有指令都连续排放在一起,形成了一个全部都是指令的区域,里面储存的是指令的操作码及定址方式等。该区域可以在硬盘上的档案中,也可以是被载入后的内存中。总之是一段指令区域,它们内部都是紧凑挨着的,内容形式完全一样,只是存放的介质不一样而已。程式码段暂存器CS就是用来指向内存中这段指令区域的起始地址。具体程式分段的解释,可见第0章。

资料段和程式码段类似,只是这段区域中的内容不是指令而是纯粹的资料,也就是说里面储存的是程式执行所需要的资料,属于指令的算子。资料段暂存器DS便是用来指向此资料区域的起始地址。

栈段是在内存中,硬盘档案中可真没有。一般的栈段是由操作系统分配指定的,所以是属于被载入到内存后才有的。本章后面还会讲栈,这里大家就先当它是一段内存区域就好。栈段暂存器SS就是用来指向此区域的起始地址。

程式码段、资料段、栈段暂存器从名字上就较容易理解,那三个附加段暂存器是干吗的?其实就是多给大家提供几个段暂存器用而已,多几个暂存器用不是更好吗,省得紧巴巴的,纯粹是为了方便大家。

值得说明的是,在16位cpu中,只有一个附加段暂存器——ES。而FS和GS附加段暂存器是在32位cpu中增加的。我们使用的是32位cpu,并不是说32位cpu在真实模式下的16位环境中就不能用FS和GS暂存器。32位的cpu是相容16位cpu的特性,就像一个小学生也可以穿中学生的衣服一样,无非是多用了个可用的资源。

IP暂存器是不可见暂存器,CS暂存器是可见暂存器。这两个配合在一起后就是cpu的罗盘,它们是给cpu导航用的。cpu执行到何处,完成要听这两个暂存器的安排。为什么要用两个暂存器?因为指令是在内存中,访问内存就要用“段:段内偏移”的形式,所以CS暂存器用来存程式码段段基址,IP暂存器用来储存程式码段段内偏移地址,同CS暂存器一样都是16位宽。

其实这两个暂存器没什么神奇的,并不是它们真的决定了cpu的航向,只是cpu的航向被存入了这两个暂存器之中。之前说过啦,指令在逻辑上是紧凑的,这样cpu便能连续不断的执行下去,天荒地老,直到断电。cpu执行完一条指令后,顺便就把下一条指令的内存地址读进来。注意看,读入的是下一条指令的内存地址,这有两个关键字,一个是内存,一个是地址。刚刚说过啦,是内存就应该用“段基址:段内偏移地址”的机制来访问。是地址就该有地方存放。您想,即使是洗菜,菜也得先找个盘子或盆之类的器具盛着再拿到水笼头下冲洗。在x86体系架构中,本着先满足“段基址:段内偏移”的形式,这个地址就分开存到了程式码段CS暂存器和指令指标IP暂存器。在执行当前指令的同时,在不跨段的情况下,cpu以“当前IP暂存器中的值+当前执行指令的机器码长度”的和做为新的程式码段内偏移地址,将其存入IP暂存器,再到该新地址处读取指令并执行。如果下一条指令需要跨段访问,还要载入新的段基址到CS暂存器。此后,继续重复以上“取址、执行”的循环。

好啦,客官以后常来玩儿哦。

本节内容摘自操作系统真象还原,请大家支援正版。

【再续】

2019-11-07 05:55:00

相关文章