- 论坛徽章:
- 0
|
本章介绍寻址技术
80x86微处理器的芯片级内存寻址,linux利用寻址硬件。
关于内存管理的三章
ch2 内存寻址
ch8 内核怎样给自己分配内存
ch9 怎样给进程分配内存
/*******************************/
1. memory address
三种地址
逻辑地址:包含在机器语言指令中用来指定一个操作数或一条指令的地址,由段和偏移量组成。(segment + offset/displacement)
线性地址:也称虚拟地址。linear address 或virtual address。是一个32位无符号整数,可表示高达4G的地址。通常用十六进制表示,0x00000000-0xffffffff。
物理地址:用于内存芯片级内存单元寻址。由32或36位无符号整数表示。
MMU(逻辑地址转换)
logical address --分段--> linear address --分页--> physical address
memory arbiter(内存仲裁器)
2. 硬件中的分段
几个术语:
Segment Selector 段选择符/子,16bits
Segment Descriptor 段描述符,8bytes
80286始,地址转换有两种方式:real mode && protected mode。下面讲protected mode下地址转换。
段选择符和段寄存器
逻辑地址(段选择符16bits + 段内偏移32bits)segment selector
段寄存器用来存放段选择符,有6个cs / ss / ds / es / fs / gs。
其中3个有专门用途:
cs 代码段寄存器,指向包含程序指令的段。(cs另一功能:含有一个两位的字段,用以指明CPU的当前特权级(CPL, current privilege level)。0代表最高优先级,3最低优先级。Linux只用0和3, 分别成为内核态和用户态)
ss 栈段寄存器,指向包含当前程序栈的段
ds 数据段寄存器,指向包含静态数据或全局数据段
其它3个段寄存器可指向任意数据段。
段描述符Segment Descriptor
每个段用8字节(64bits)的段描述符表示,段描述符存放在GDT和LDT中。
GDT只有一个,而每个进程除了GDT中的段之外,如果需要还可以拥有自己的LDT。
GDT的地址和大小存放在gdtr控制寄存器中,当前正使用的LDT存放在ldtr控制寄存器中。
Linux常用的几种类型:
数据段描述符
代码段描述符
任务状态段描述符TSSD(任务状态段TSS)
局部描述符表描述符(LDTD)
快速访问段描述符
段寄存器仅仅存放逻辑地址的段选择符。为了加速逻辑地址到线性地址的转换,80x86提供一种附加的非编程的寄存器(不能被程序员设置), 供6个可编程的段寄存器使用。每个非编程寄存器含有8个字节的段描述符,由相应的段寄存器中的段选择符来指定。
当一个段选择符装入段寄存器时,相应的段描述符就装入对应的非编程寄存器。这样,针对该段的逻辑地址转换就可以不访问内存中的GDT或LDT,而是直接引用寄存器(除非段寄存器内容改变),从而加速。
段描述符在GDT / LDT内的相对地址 = index * 8
例:若gdtr寄存器中GDT为0x00020000,段选择子index为2。则段描述符地址为0x00020010
线性地址 = 段描述符中BASE字段的值 + offset
注意:有了与段寄存器相关的不可编程寄存器,只有当段寄存器内容改变时才需前两个操作。
3. linux中的分段
分段和分页都可以划分进程的物理地址空间:
分段可以给每个进程分配不同的线性地址空间
分页可以把同一线性地址空间映射到不同的物理空间
与分段相比,linux更喜欢使用分页,因为…
用户态进程使用用户代码段和用户数据段进行指令和数据寻址。
内核态进程…
相关宏__USER_CS,__USER_DS,__KERNEL_CS,__KERNEL_DS,装进相应寄存器即可
用户态和内核态所有进程可以使用相同的逻辑地址。
所有段都从BASE相同,说明linux下逻辑地址与线性地址是一致的。
Linux GDT
单处理器中只有一个,多处理器系统中每个cpu对应一个gdt。
Linux LDT
大多数用户态下的程序不使用LDT,于是内核定义了一个缺省的LDT供大多数进程共享。缺省的LDT存放在default_ldt数组中。
需要时modify_ldt()系统调用允许进程创建自己的LDT。
4. 硬件中的分页
页page
分页paging
页框page frame
页目录表page directory
页表page table
分页单元paging unit把线性地址转换为物理地址。
其中一个关键任务是,把所请求的访问类型与访问权限相比较,如果无效就产生缺页异常。
线性地址被分为页page。
页内部连续的线性地址被映射到连续的物理地址中。
paging unit把所有的RAM分成固定长度的页框(page frame)(有时也叫物理页)。
每个页框包含一个页。
页和页框不同,页只是个数据块,可以存放在任何页框或磁盘中。
把线性地址映射到物理地址的数据结构称为页表(page table)页表存放在主存中。
80386开始,所有的80x86都支持分页,它通过设置cr0寄存器的PG标志启用。PG = 0 时,线性地址就被解释为物理地址。
常规分页
页大小4KB,32位线性地址分成3个域。
页目录项和页表项结构相同,包含Present标志等(14号异常:缺页中断)
二级模式转换。
正在使用的页目录的物理地址存放在控制寄存器cr3中。
Offset字段12位长,故每页含有4096字节的数据。
1024 * 1024 * 4096 = 2的32次幂 个存储单元
扩展分页(extended paging)
允许页框大小为4MB, 这可以实现把大块连续的线性地址转换成相应的物理地址。从而地址转换容易,节省内存并保留TLB项。
硬件保护方案
页寻址的控制(User/Supervisor标志 && CPL标志)
段有3种存取权限(读写执行),页有2种存取权限(读写)
常规分页举例(很好)
PAE分页机制(物理地址扩展)
设置cr4中的PAE标志激活。
64位系统中的分页
硬件高速缓存
转会后援缓冲器(TLB : translation lookaside buffer)
5. linux中的分页
为支持64位系统,Linux2.6.10开始采用三级分页模型,2.6.11开始采用四级分页模型。
对于未启用PAE的32位系统,两级页表足够了。
线性地址字段
宏PAGE_SHIFT PMD_SHIFT PUD_SHIFT PGDIR_SHIFT PTRS_PER_PTE等
页表处理
pte_t pmd_t pud_t pgd_t 表示4种页表项的格式
类型转换宏(__pte对应pte_val __pmd对应pmd_val 等,共5对)
物理内存布局
进程页表
进程的线性地址空间分为两部分:
Linear addresses from 0x00000000 to 0xbfffffff can be addressed when the process runs in either User or Kernel Mode.(0--3G)
Linear addresses from 0xc0000000 to 0xffffffff can be addressed only when the process runs in Kernel Mode.(3G--4G)
#define PAGE_OFFSET 0xc0000000
内核页表(临时内核页表 、最终内核页表(RAM(-896, 896-4096, 4096-))
swapper_pg_dir变量
宏__pa把从PAGE__OFFSET开始的线性地址转换成相应的物理地址,__va相反
linux在初始化阶段映射896MB的RAM窗口到内核线性地址空间,剩余RAM不映射。如果一个程序需要对RAM的其余部分寻址,那就必须把某些其它的线性地址间隔映射到所需的RAM,这意味着对某些页表项进行修改,即第八章的动态重映射。
线性地址的最高128MB留给其它集中映射(非连续内存分配、固定映射等)去用,因此1G – 128M = 896M
固定映射的线性地址(fix-mapped linear address)
固定映射的线性地址可以映射任何物理地址,比指针更有效。
而第4G开始的前896M建立的映射是线性的,其物理地址 = 线性地址X – PAGE_OFFSET。
fix_to_virt(const unsigned int idx)函数计算从给定索引开始的常量线性地址。
处理硬件高速缓存和TLB
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/74638/showart_1104040.html |
|