remark:《understanding the linux kernel》笔记第二篇,对应原书第二章。
x86 内存地址
x86有三种内存地址:
- logical Address: segment: offset(48bit),段基址+ 偏移
linear Address: 0x00000000-0xffffffff
前两个都是虚拟地址,不是实地址
Physical address 真实的内存单元的地址,32-bit或者36-bit
MMU (the Memory Management Unit) 内存管理单元
MMU 是一个硬件,它主要做地址翻译:
Segmentation in Hardware 硬件分段机制
段选择子是16bit的段寄存器,分成3部分:
- RPL(0-1): 请求特权级(request privilege level)与段访问权限有关
- TI(2): table indicator (0在GDT中,否则在LDT中)
- Index(3-15): 段索引
有6个段寄存器:cs(code segment),ss(stack seg),ds(data seg),es,fs,gs
segmentation descriptor 段描述符
每个段描述符占8-bytes
上面所列出来的每个field 都在原书上有标明含义,见Table 2-1
还有两个段描述符表,GDT(全局段描述符表)总共256( )项被所有进程共享,LDT局部段描述表,他们的起始地址存在 gdtr和ldtr 寄存器中
Fast Access to Segment Descriptors
这是加快地址翻译用的,其实就是加了一个缓存
右边的不可编程寄存器(nonprogramable register) 缓存了左边的翻译地址,使得不必每次都到内存中去取段描述符
segmentation unit
逻辑地址转化为物理地址的流程:
由于不可编程寄存器(见上图)的存在,前两步仅在段描述符改变是才会访问内存
segmentation in Linux linux中的分段机制
linux 将段机制给弱化了,主要出于以下两个原因:
- 内存管理更加简单是,如果他们共享同样的线性地址空间
- 适应不同的体系结构
这是linux主要的几个段
linux 中所有进程共享逻辑地址空间(0x0-0xffffffff)
GDT & LDT
GDT没啥好说的(这个表可以再内核代码中找到),ldt linux中设计了一个default_ldt 被most process 使用,如果进程要用自己的ldt 可以用modify_ldt创建
Paging in Hardware
控制寄存器 cr0 中有一个 PG 位,当PG=0 时,线性地址就是物理地址,页机制没有开启,PG=1 页机制被开启
常规的32-bit的页管理,分了2级页表,由于页表在内存中,每次访问都需要3次内存访问才能访问到物理地址,不过由于硬件缓冲 TLB我们可以加快页的访问.
Paging in Linux
linux 可以用4级页表,取决于cpu的体系结构
这一节我并没有理解多少内容,号多细节,涉及源码我都跳了,过去,接下来说一说,我所理解的那一部分内容
物理内存布局
还记的系统是怎么开机的吗?(参见《understanding the linux kernel》笔记: 1. linux开机发生了什么?)最后一步是内核初始化,这里内核需要建立物理映射,标记哪些是可用的内存地址,哪些是不可以用的。内核代码从第2M 开始,即 0x100000,第一M 通常是一些系统参数和BIOS以及体系结构相关的东西,出于为了让内核代码连续的考虑将内核代码放在 0x100000开始出,前3M布局如下图