现在的位置: 首页 > 综合 > 正文

内存管理与“三地址”的千丝万缕(2)

2014年11月27日 ⁄ 综合 ⁄ 共 2798字 ⁄ 字号 评论关闭
内存管理与“三地址”的千丝万缕(2)

好,开始正题!

 cpu的内存管理模式。对于8086,大家很容易知道利用段基址加逻辑地址(偏移地址)来找到物理地址的。而段基址又有分为4种,即DSCSSSES。每段的起始地址在开始时由操作系统定义了。这就是所谓的段机制。在8086的运行中,物理地址的形成因操作而异,取指令时cpu会用CSIP的内容相加形成指令所在的单元的20位地址,而进行堆栈时会用SSSP(或基址指针BP)的内容相加来形成20位物理地址。在这里我们扯到一个很好的很有用的概念,这将会在linux系统与arm中提到。内存分段为程序的浮动装配创造了条件:即要求程序不涉及物理地址,进一步讲,就是要求程序与段地址没有关系,而只与偏移地址有关系,这样的程序装在哪一段都能运行了,只需在程序中不涉及段地址就行了,凡是遇到转移指令或调用指令则都采用相对转移或相对调用。(当你看到一些说是与地址无关指令时再回头看这段文字你就很明白了)!

 而对于32位的cpu,那就相对麻烦些了!我们从cpu的运作来说(含操作系统,这里选了linux)吧!第一步,开机加电后,工作在实模式下,这时还是8086的模式,即地址是0x000000xfffffcpu先从0xffff0(这是物理地址)执行,利用BIOS将存放在硬盘中的bootsect.s复制到内存中,并设置中断向量表。而后是执行bootsect.s,将setup.s和内核程序复制到内存中。执行setup.s转入保护模式(为什么成为保护模式呢?因为在这种模式下,cpu将程序分为了4个级别,linux只采用了2种级别,那就是内核特权级以及用户级,操作系统便是内核特权级,在这种模式下,能起到更好保护操作系统不被更改的目的,所以谓之“保护”,在这里提一下,一些病毒便是利用这种级别不同来侵入电脑),这时进入保护模式后执行head.s,创建页表,页目录,GDTIDTLDT等,一旦这些设置完,便有了虚拟地址了。

 设置完后就进入了内核的main.c函数中,第一个执行的函数是start_kernel的函数。OK!以后便是创建进程以及撤销进程的事了。你打开一个应用程序便是创建新进程,记住这些新进程都是进程0通过一个个子进程得来的,所以有了父进程与子进程的概念。程序运行时,cpu虚拟地址逻辑地址访问主存(内存),在此过程中先通过硬件和软件找出虚拟地址到物理地址的关系,判断要访问的单元的内容是否已装入内存中,如果是,直接访问,否则说明出现虚拟地址往物理地址转换故障,此时会出现一个异常中断叫缺页中断,然后调用中断处理程序,把要访问的单元及有关数据从辅存(软盘或是硬盘)调入内存,覆盖掉主存中原有的一部分数据,然后将虚拟地址转换成物理地址。这样就实现了内存好像很大的样子,因为存储管理单元会将用到的程序以及数据一块块的调入内存中。

 现在有人也许会问:那怎么样才能实现三者之间的转换呢?

 逻辑地址转换成线性地址:48位的逻辑地址(程序常常以32位的偏移地址出现)包含了16位的段选择子以及32位的偏移地址,16位的段选择子用来索引全局描述符表(GDT)或局部描述符表(LDT),每一个表项有8字节的长度,里面包含了段基址,段界限(段限长)、段的属性。段选择子并不是全部都用来索引的,其中只有13位用来索引,所以可以索引8192*2个符表=16384。然后呢,便用索引的符表项中的段基址加上偏移地址就得到线性地址了!更具体的操作过程是:在程序中,一个偏移量可能由立即数和另外一、二个寄存器给出的值构成,分段部件把各地址分量送到一个加法器中,形成有效地址,然后再经过另一个加法器和段基址相加,得到线性地址,同时嗨通过一个32位的减法器和段的界限值比较。检查是否越界。

线性地址转换成物理地址(保护模式下):现在有了线性地址,32位的线性地址中高十位用来索引页目录表,中间十位用来索引页表,最后十二位是页面内的偏移值,页目录每一个页目录项是四字节,共有4KB,即1024项。目录项里有页表的基地址(页表也是4KB,但有4个页表),而后加上线性地址的中间十位即页表的偏移地址得到索引的页表项,页表项里又含有页的基址,加上线性地址的最后十二位值得到物理存储器的物理地址,因为是十二位偏移,所以每页是4KB。这只是简单的过程,具体的大家还是要参考别的书籍了!

现在讲一下arm(以S3C2410为例)的内存管理机制,这和cpu很相似,要是你懂了cpu的,这就不在话下了,其中有些许的细节差别我略微的讲一下。

你发现有一点很明显的区别了吗?Pentium的所有外设是独立编址的,但是arm9却不是这样,它利用存储控制器将外设的访问地址统一起来,这其中就包括了NandFlashSDRAM,这也正是嵌入式的魅力,利用4G的空间分配给所有外设,或者可以按pentium的观点来说便是“存储器与外设统一编址”(记住,两者不是一样的啊,这里只是形象称呼而已),这在嵌入式系统中可以节省很多元件,使体积大大减少,符合嵌入式系统的定义。

Arm9的页机制与intel的很类似,,用表格存储虚拟地址对应的物理地址。有一个细微的差别是页的大小分为三种:大页(64KB)、小页(4KB)、极小页(1KB)。(尽管pentium的页大小也是可调的,但是只有4KB4MB供选择,这通过CR4控制寄存器的PSE位),另外arm9的页表还分为粗页表与细页表。其他的包括段寻址是一样的。顺便提一下,给定的虚拟地址转换成线性地址中,虚拟地址的各位代表的意义也是与pentium不一样的,这同样体现在线性地址转换成物理地址。这里就不再赘述了,可以参考arm9的书籍。

还有就是arm9TLBcache的问题,这里就不提了,这个很容易理解的,它与pentium一样。两者都是为了让cpu执行得更快而做出的设计。

现在讲一下使用arm9时一个很重要的细节。在通常的bootloader中,未开启MMU前,我们程序用的地址都是物理地址,开启之后就变成了虚拟地址,这就涉及到bootloader中有一些程序的物理地址必须与虚拟地址一样,这样才不致于开启MMU后程序执行错了。具体实现是在页表设置中实现。当数据和代码还在NandFlash中时,内存中没有数据,读取数据时便需要地址转换,这在bootloaderlowlevel_init.s程序中体现了,代码如下:

Ldr r0,=SMRDATA

Ldr r1,_TEXT_BASE

Sub r0,r0,r1

Ldr r1,BWSCON

Add r2,r0,#13*4

这时内存中(SDRAM)没有数据,不能使用连接脚本程序里面的确定地址读取数据。

以上就是我最近学习linux的总结,这并不是最终版本,一旦有新的想法,我会及时更新这篇文章!这里估计有很多错误的,欢迎大家指正批评!

抱歉!评论已关闭.