我曾经提到过,理解一个嵌入式平台的关键是它的系统映射表(System Memory Map)。计算机的核心是CPU,然而CPU所做的也主要是计算,至于要计算的内容(数据),算法(代码),和结果都是存放在外部存储器中,或输出到设备。所有这些目标的寻址是很关键的。
当然,这一章主要讲内存寻址。现代处理器增加了硬件电路来帮助操作系统更高效的管理内存。包括支持,每个进程有共同的虚拟地址范围,但是对应不同的物理地址,还包括,内存读写保护等等。
一。地址分类
地址分为三类:
1. 逻辑地址,机器代码中数据或指令的地址,包括段和偏移
2. 虚拟地址,也称线性地址,每个进程都有自己独立的地址空间,该地址空间是虚拟地址空间,通过页表可以映射到物理地址
3. 物理地址,处理器访问内存时,地址线上的信号即物理地址,是系统真实的地址
MMU把逻辑地址先转换成线性地址(通过段逻辑电路),最后转换成物理地址(通过页逻辑单元)。
二。段逻辑
逻辑地址转换成线性地址,通过段逻辑。首先,逻辑地址中的段对应了段描述符,段描述符中的段线性基地址加上逻辑地址中的偏移,就成了线性地址。段描述符会装载到寄存器中。
2.1 段逻辑在Linux中的应用
考虑到对各种架构的兼容性,Linux没有用到段的逻辑。Linux所有进程包括内核,它们的段(代码段,数据段等)的线性基地址都是0,所以逻辑地址的偏移即线性地址。
Linux唯一用到了代码段寄存器中两位,指示目前的代码是应用代码或内核代码。它帮助CPU确定是否可以访问某些内存等。
三。页逻辑单元
页逻辑单元把线性地址(虚拟地址)转换成物理地址。
应用程序地址空间,或内核地址空间,这里面的地址,32位地址分成三部分,10位是页目录,10位是页表,12位是偏移。每个应用程序都有一个页目录(1024目录项),每个目录项指向一个页表(1024个页表项),地址转换时,通过目录项找到页表,通过页表找到页,页的起始物理地址加上偏移地址得到真实的物理地址。
所以,每个应用程序都有自己独立的页目录和页表,完成虚拟地址到物理地址的转换。应用程序申请内存时,又内核分配好虚拟地址,当访问内存时,会产生缺页,内核找到空闲的物理页,更新到页表中。
除此之外,每个页表项有以下信息,页是否存在,是否脏(Dirty),读写权限,需要的CPU权限。
当段寄存器表明现在是用户代码,如果访问的页需要超级权限,就无法访问。
3.1 PAE(PhysicalAddress Extension)
该技术主要是为了支持32位微处理器访问64G内存空间。当然,每个进程依旧是4G内存空间,只是内核有更多的内存分配给不同的应用程序。
3.2 64位架构的分页技术
和32位一样,只是更多层级。32位分为3项,目录项,页表项,和偏移。而64位,需要更多层级,多数为4层。
3.3 高速缓存
高速缓存虽然不是内存,但是它的设计是为了提高内存的使用效率。高速缓存被分为很多行,CPU访问物理地址时,先在高速缓存里面查找,如果在则Cachehit(命中),否则Cachemiss。
3.4 TLB (Translation Lookaside Buffers)
TLB也是Cache的一种,它的目的是缓存已经访问过的虚拟地址对应的页表。当一个应用程序用了太多内存,需要太多页表项,从而TLB需要不断换入换出时,这会降低应用性能,此时可以考虑用LargePage(2M的页)来减少页表项。
四,页在Linux下的具体应用
4.1 物理内存布局
Linux启动时从Bios了解到物理内存布局,排除被用的内存页(比如内核代码数据空间),其它页被管理起来供分配给缺页等情况下使用。
4.2 Linux提供对Cache和TLB的操作
略。