操作系统之CPU与实模式(上)

电子说

1.3w人已加入

描述

对于人类来说,我们不喜欢拐弯抹角,喜欢更直接的东西,“有话直说”、“没有中间商赚差价”、“简洁的设计”等等,然而对于计算机,尤其是对内存管理来说则恰恰相反, 在这里"简洁"的设计往往不是好的设计 ,这到底是什么意思呢?

我们在很早的文章中就提到过,内存从本质上将非常简单,你可以将其想像成一个个的小盒子组成,每个小盒子要么能存储1要么存储0,每8个小盒子组成一个字节(8比特),每个字节都有一个唯一的地址,通过这个地址我们就能从相应的一组小盒子取出这个比特。

其它没了。

看到了吧,内存本身其实是非常简单的,然而程序员以及程序使用内存的方式又让这个问题变得复杂起来,分析任何复杂问题都要抓住重点、抓住核心问题,那么这里的重点以及核心是什么呢?

不卖关子,这里的核心在于两个字: 寻址 ,Addressing。

一切都是围绕寻址展开的。

寻址,最重要的就是寻址

什么是寻址 Addressing?所谓寻址就是找到内存中某个我们需要的数据的方式。

哪怕以我们平时去储物柜取东西都有很多“寻址”方式:

  • 直接告诉我们一个编号,我们拿到这个编号后按个去找,就像下面这张图,我们需要找到东西在第15号储物柜中,那么我们根据15这个地址就能找到第15号储物柜。
    cpu

  • 当然我们也可以将储物柜划分区域,还是以刚才的储物柜为例我们可以划分为3个区域,当我们需要找东西时告诉我们其在储物柜的哪个区域,以及在该区域中的"偏移"是多少。

    以下图为例我们需要的东西在第二个区域,区域内的偏移为6(该区域中的第6个储物柜)。

    cpu

实际上,第一种更像是“绝对寻址”,什么意思呢?就是找到某个具体的储物柜是根据一个“写死的地址”(hardcode),很死板,第二种更像是相对寻址,稍显灵活一些。

怎么样,你是不是感觉这两种其实也没什么区别嘛,的确,对于找储物柜这个例子来说这两种方式的确没什么区别,但对于内存来说就不太一样了。

死板 vs 灵活

我们知道程序以及程序使用的数据编译好后存放在磁盘上,运行时要加载到内存中,因此这里同样存在寻址问题:我们需要根据内存地址找到机器指令以及数据,接下来假设有一个只有8字节大小的内存和一个只有2字节机器指令的程序(无需关心实际意义):

cpu

这段2字节的代码非常简单,其实就是一个无意义的while循环,注意看这里的jmp这条指令,我们直接跳转到内存地址2,这就是一个写死(hard code)的内存地址,这就意味着我们必须把该程序加载到内存地址为2的位置上:

cpu

否则这段指令根本没有办法运行,比如我们把这段代码加载到内存地址6上去:

cpu

那么在执行jmp 2时我们根本没有办法跳转到add这行指令,有的同学可能觉得无所谓,不就是内存地址写死了嘛,好像也没什么大不了的吧。

如果一次只能运行一个程序的确也没什么大不了的,但对于操作系统最核心的功能之一:多任务,也就是一次可以运行多个程序来说这个方案简直行不通。

在这种方案下你几乎没有办法一次运行多个程序,除非在运行之前你给要运行的这几个程序划定好区域,比如要运行两个程序A和B,A占用03这个区域的内存;B占用46这个区域的内存, 对于现代程序员来说你能想象在程序运行之前就需要给它划定好区域吗? 显然,这非常繁琐,也容易出错。

如果你在上世纪六七十年代写代码,面临的大概就是这样一种状况。

实际上这个问题的核心就在于 重定位程序使用的地址不能绑定在一个内存区域上,需要足够灵活,我们需要在不修改代码的情况下把程序加载到任意内存区域上运行! 想一想该怎么解决这个问题。

作为程序员肯定和文件路径打过交道,如果你能明白绝对路径与相对路径就能解决重定位问题。

绝对路径与相对路径

想一想绝对地址有什么问题?这个问题就好比你在程序中读取一个绝对地址时:

/user/xiaofeng/doc/a.c

如果是你自己的计算机那么没有问题, 但如果这个程序在其它人的计算机上运行就不一定了,因为其它人的计算机中不一定有这个路径 ,这时该怎么办呢?聪明的你一定知道,那就不要使用绝对路径,而是使用相对路径就可了:

./a.c

其中./表示程序运行时所在的路径,这时不管这个程序在哪个路径下运行都能找到a.c这个文件,这时所在的目录就成为了 基准

解决重定位这个问题也是同样的道理,编程生成可执行程序时不再使用 绝对内存地址 ,而是使用相对地址,怎么使用相对地址呢?相对于谁呢?很简单, 相对于该程序被加载到的内存起始地址

此时我们的jmp命令后面不再是一个绝对的内存地址,而是一个相对地址:0,但毕竟向内存发出读写指令时必须使用一个内存地址,那么CPU执行jmp 0时该怎样将其转为一个内存地址呢?

cpu

很简单,因为这一段程序被加载到了内存起始地址2,因此只需要用相对地址加上起始地址得到的就是真实的物理内存地址:

物理地址 = 起始地址 + 相对地址

很简单吧, 这样不管这段程序被加载到了哪个内存区域,只要我们知道起始地址那么总能计算出真实的物理内存地址 ,重定位问题就可以这样解决。

实际上你会发现, 这个储物柜的第二种寻址方式也没有什么区别

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分