Linux页表机制分析.docx_第1页
Linux页表机制分析.docx_第2页
Linux页表机制分析.docx_第3页
Linux页表机制分析.docx_第4页
Linux页表机制分析.docx_第5页
已阅读5页,还剩23页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

12.页表机制12.1.引言在Linux系统中,存在以下三种地址: 逻辑地址:它被包含在机器指令中用来指定一个操作数或一条指令的地址。每一个逻辑地址都由一个段(Segment)和偏移量(Offset)组成,偏移量指明了从段开始的地方到实际地址之间的距离。 线性地址(虚拟地址):一个32位无符号整数,可以用来表示高达4GB的地址。线性地址通常用十六进制数字表示,值的范围为0x00000000, 0xffffffff)。 物理地址:用于内存芯片级内存单元寻址。它们与从CPU的地址引脚发送到内存总线上的电信号相对应。物理地址由32位或36位无符号整数表示。内存控制单元(MMU)通过一种称为分段单元的硬件电路把一个逻辑地址转换成线性地址;称为分页单元的硬件电路把线性地址转换成一个物理地址。有些MMU 没有分页单元,或者禁止使能分页单元,比如x86的实模式,那么就只有分段单元,那么经过分段单元转换后的地址就是物理地址。有些MMU没有分段单元,大 多数RISC架构的CPU就是如此,此时段基址相当于0,而代码中的偏移地址就是线性地址,所有Linux下逻辑地址和线性地址是一致的。如下图所示: 图60.地址转换Linux中以非常有限的方式使用分段。运行在用户态的所有Linux进程都使用一对相同的段来对指令和数据寻址,它们的段基址分别是_USER_CS 和_USER_DS。与此同时,运行在内核态的所有Linux进程(内核线程)都使用一对相同的段对指令和数据寻址,它们的段基址分别 _KERNEL_CS和_KERNEL_DS。 分段可以给每个进程分配不同的线性地址空间,分页可以把同一线性地址空间映射到不同的物理空间。与分段相比,Linux更喜欢分页方式,因为: 当所有进程使用相同的段寄存器值时,内存管理变得更简单,也就是说它们能共享同样的一簇线性地址。 为了兼容绝大多数的CPU,RISC体系架构对分段的支持很有限,比如ARM架构的CPU中的MMU单元通常只支持分页,而不支持分段。分页使得不同的虚拟内存页可以转入同一物理页框。于此同时分页机制可以实现对每个页面的访问控制,这是在平衡内存使用效率和地址转换效率之间做出的 选择。如果4G的虚拟空间,每一个字节都要使用一个数据结构来记录它的访问控制信息,那么显然是不可能的。如果把4G的虚拟空间以4K(为什么是4K大 小?这是由于Linux中的可执行文件中的代码段和数据段等都是相对于4K对齐的)大小分割成若干个不同的页面,那么每个页面需要一个数据结构进行控制, 只需要1M的内存来存储。但是由于每一个用户进程都有自己的独立空间,所以每一个进程都需要一个1M的内存来存储页表信息,这依然是对系统内存的浪费,采 用两级甚至多级分页是一种不错的解决方案。另外有些处理器采用64位体系架构,此时两级也不合适了,所以Linux使用三级页表。 页全局目录(Page Global Directory),即 pgd,是多级页表的抽象最高层。每一级的页表都处理不同大小的内存。每项都指向一个更小目录的低级表,因此pgd就是一个页表目录。当代码遍历这个结构时(有些驱动程序就要这样做),就称为是在遍历页表。 页中间目录 (Page Middle Directory),即pmd,是页表的中间层。在 x86 架构上,pmd 在硬件中并不存在,但是在内核代码中它是与pgd合并在一起的。 页表条目 (Page Table Entry),即pte,是页表的最低层,它直接处理页,该值包含某页的物理地址,还包含了说明该条目是否有效及相关页是否在物理内存中的位。12.2.一级页表三级页表由不同的的数据结构表示,它们分别是pgd_t,pmd_t和pte_t。注意到它们均被定义为unsigned long类型,也即大小为4bytes,32bits。 arch/arm/include/asm/page.htypedef unsigned long pte_t;typedef unsigned long pmd_t;typedef unsigned long pgd_t2;typedef unsigned long pgprot_t;以下是页表操作相关的宏定义。 #define pte_val(x) (x)#define pmd_val(x) (x)#define pgd_val(x) (x)0)#define pgprot_val(x) (x)#define _pte(x) (x)#define _pmd(x) (x)#define _pgprot(x) (x)任何一个用户进程都有自己的页表,与此同时,内核本身就是一个名为init_task的0号进程,每一个进程都有一个mm_struct结构管理进程的内 存空间,init_mm是内核的mm_struct。在系统引导阶段,首先通过_create_page_tables在内核代码的起始处_stext 向低地址方向预留16K,用于一级页表(主内存页表)的存放,每个进程的页表都通过mm_struct中的pgd描述符进行引用。内核页表被定义在 swapper_pg_dir。 arch/arm/kernel/init_task.c#define INIT_MM(name) .mm_rb = RB_ROOT, .pgd = swapper_pg_dir, .mm_users = ATOMIC_INIT(2), .mm_count = ATOMIC_INIT(1), .mmap_sem = _RWSEM_INITIALIZER(name.mmap_sem), .page_table_lock = _SPIN_LOCK_UNLOCKED(name.page_table_lock), .mmlist = LIST_HEAD_INIT(name.mmlist), .cpu_vm_mask = CPU_MASK_ALL, struct mm_struct init_mm = INIT_MM(init_mm); swapper_pg_dir在head.S中被定义为PAGE_OFFSET向上偏移TEXT_OFFSET。TEXT_OFFSET代表内核代码段的 相对于PAGE_OFFSET的偏移。KERNEL_RAM_VADDR的值与_stext的值相同,代表了内核代码的起始地址。 swapper_pg_dir为KERNEL_RAM_VADDR - 0x4000,也即向低地址方向偏移了16K。 arch/arm/Makefiletextofs-y := 0x00008000.TEXT_OFFSET := $(textofs-y)特定系统架构的Makefile中通过textofs-y定义了内核起始代码相对于PAGE_OFFSET的偏移。 arch/arm/kernel/head.S#define KERNEL_RAM_VADDR(PAGE_OFFSET + TEXT_OFFSET).globl swapper_pg_dir.equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000ARM Linux中的主内存页表,使用段表。每个页表映射1M的内存大小,由于16K / 4 * 1M = 4G,这16K的主页表空间正好映射4G的虚拟空间。内核页表机制在系统启动过程中的paging_init函数中使能,其中对内核主页表的初始化等操作 均是通过init_mm.pgd的引用来进行的。在系统执行paging_init之前,系统的地址空间如下图所示: 图61.内核RAM布局图中的黄色部分就是内核0号进程的主页表。 arch/arm/mm/mmu.cvoid _init paging_init(struct meminfo *mi, struct machine_desc *mdesc)void *zero_page;build_mem_type_table();sanity_check_meminfo(mi);prepare_page_table(mi);bootmem_init(mi);devicemaps_init(mdesc);top_pmd = pmd_off_k(0xffff0000);zero_page = alloc_bootmem_low_pages(PAGE_SIZE);memzero(zero_page, PAGE_SIZE);empty_zero_page = virt_to_page(zero_page);flush_dcache_page(empty_zero_page);图62.ARM内存主页表初始化paging_init依次完成了以下工作: 调 用prepare_page_table初始化虚拟地址0, PAGE_OFFSET和mi-bank0.start + mi-bank0.size, VMALLOC_END所对应的主页表项,所有表项均初始化为0。这里保留了内核代码区,主页表区以及Bootmem机制中的位图映射区对应的主页表。 这是为了保证内核代码的执行以及对主页表区和位图区的访问。如果只有一个内存bank,那么mi-bank0.start + mi-bank0.size的值和high_memory保持一致,它是当前bank进行物理内存一一映射后的虚拟地址。对于一个内存为 256M的系统来说,它只有一个bank,经过prepare_page_table处理后的内存如上图所示。 接着在bootmem_init函数将通过bootmem_init_node对每一个内存bank的页表进行值的填充。bootmem_init_node将通过map_memory_bank间接调用create_mapping,最终由该函数创建页表。 通过devicemaps_init初始化设备I/O对应的相关页表。 最后创建0页表,并在Dcache中清空0页表的缓存信息。图63.页表创建函数调用12.3.ARM 内存访问当ARM要访问内存RAM时,MMU首先查找TLB中的虚拟地址表,如果ARM的结构支持分开的地址TLB和指令TLB,那么它用: 取指令使用指令TLB 其它的所有访问类别用数据TLB指令TLB和数据TLB在ARMv6架构的MMU中被分别称为指令MicroTLB和数据MicroTLB。如果没有命中MicroTLB,那么将查询主TLB,此时不区分指令和数据TLB。 如果TLB中没有虚拟地址的入口,则转换表遍历硬件从存在主存储器中的转换表中获取转换页表项,它包含了物理地址或者二级页表地址和访问权限,一旦取到, 这些信息将被放在TLB中,它会放在一个没有使用的入口处或覆盖一个已有的入口。一旦为存储器访问的TLB 的入口被拿到,这些信息将被用于: C(高速缓存)和B(缓冲)位被用来控制高速缓存和写缓冲,并决定是否高速缓存。 首先检查域位,然后检查访问权限位用来控制访问是否被允许。如果不允许,则MMU 将向ARM处理器发送一个存储器异常;否则访问将被允许进行。 对没有或者禁止高速缓存的系统(包括在没有高速缓存系统中的所有存储器访问),物理地址将被用作主存储器访问的地址。图64.高速缓存的MMU存储器系统12.4.ARM MMU页表在ARMv6的MMU机制中,提供了两种格式的页表描述符: 兼容ARMv4和ARMv5 MMU机制的页表描述符。这种描述符可以对64K大页面和4K小页面再进一步细分为子页面。 ARMv6特有的MMU页表描述符,这种页表描述符内增加了额外的特定比特位:Not-Global(nG),Shared (S),Execute-Never (XN)和扩展的访问控制位APX。图65.向前兼容的一级页表描述符格式图66.向前兼容的二级页表描述符格式Linux使用ARMv6特有的MMU页表描述符格式,它们的标志位描述如下: 图67.ARMv6一级页表描述符格式表20.ARMv6一级页表描述符比特位含义位标志含义b1:0类型指示页表类型:b00 错误项;b11 保留;b01粗页表,它指向二级页表基址。b10:1MB大小段页表(b18置0)或16M大小超级段页表(b18置1)b2Ba写缓冲使能bb4Execute-Never(XN)禁止执行标志:1,禁止执行;0:可执行b5:8域(domain)指明所属16个域的哪个域,访问权限由CP15的c3寄存器据定b9P(ECC Enable)ECC使能标志,1:该页表映射区使能ECC校验cb10:11AP(Access Permissions)访问权限位,具体见访问权限列表b12:14TEX(Type Extension Field)扩展类型,与B,C标志协同控制内存访问类型bit15APX(Access Permissions Extension Bit)扩展访问权限位bit16S(Shared)共享访问bit17nG(Not-Global)全局访问bit180/1段页表和超级段页表开关bit19NSa 高 速缓存和写缓存的引入是基于如下事实,即处理器速度远远高于存储器访问速度;如果存储器访问成为系统性能的瓶颈,则处理器再快也是浪费,因为处理器需要耗 费大量的时间在等待存储器上面。高速缓存正是用来解决这个问题,它可以存储最近常用的代码和数据,以最快的速度提供给CPU处理(CPU访问Cache不 需要等待)。 b SBZ意味置0,该位在粗页表中置0。c ARM1176JZF-S处理器不支持该标志位。Linux 在ARM体系架构的Hardware page table头文件中通过宏定义了这些位。 arch/arm/include/asm/pgtable-hwdef.h/* * Hardware page table definitions. * * + Level 1 descriptor (PMD) * - common */#define PMD_TYPE_MASK (3 0) / 获取一级页表类型的掩码,它取bit0:1#define PMD_TYPE_FAULT (0 0) / 置bit0:1为b00,错误项#define PMD_TYPE_TABLE (1 0) / 置bit0:1为b01,粗页表#define PMD_TYPE_SECT (2 0) / 置bit0:1为b10,段页表#define PMD_BIT4 (1 4) / 定义bit4,禁止执行标志位#define PMD_DOMAIN(x) (x) 5) / 获取域标志位b5:8#define PMD_PROTECTION (1 9) / b9ECC使能标志以上定义了一级页表的相关标志位。Linux使用段页表作为一级页表,粗页表作为二级页表的基址页表。段页表的标志位定义如下: #define PMD_SECT_BUFFERABLE (1 2)#define PMD_SECT_CACHEABLE (1 3)#define PMD_SECT_XN (1 4) /* v6 */#define PMD_SECT_AP_WRITE (1 10)#define PMD_SECT_AP_READ (1 11)#define PMD_SECT_TEX(x) (x) 12) /* v5 */#define PMD_SECT_APX (1 15) /* v6 */#define PMD_SECT_S (1 16) /* v6 */#define PMD_SECT_nG (1 17) /* v6 */#define PMD_SECT_SUPER (1 18) /* v6 */图68.ARMv6二级页表基址格式二级页表相同标志位的含义与一级页表相同,这里不再单独列出。注意它的b1为1时,b0表示禁止执行标志。Linux对二级页表中的标志位定义如下: /* * + Level 2 descriptor (PTE) * - common */#define PTE_TYPE_MASK (3 0) / 获取二级页表类型的掩码,它取bit0:1#define PTE_TYPE_FAULT (0 0) / 置bit0:1为b00,错误项#define PTE_TYPE_LARGE (1 0) / 置bit0:1为b01,大页表(64K)#define PTE_TYPE_SMALL (2 0) / 置bit0:1为b10,扩展小页表(4K)#define PTE_TYPE_EXT (3 0) / 使能禁止执行标志的扩展小页表(4K)#define PTE_BUFFERABLE (1 2) / B标志#define PTE_CACHEABLE (1 3) / C标志Linux二级页表使用扩展小页表,这样每个二级页表可以表示通常的1个页面大小(4K)。Linux对二级页表标志位的定义如下: /* * - extended small page/tiny page */#define PTE_EXT_XN (1 0) /* v6 */#define PTE_EXT_AP_MASK (3 4)#define PTE_EXT_AP0 (1 4)#define PTE_EXT_AP1 (2 4)#define PTE_EXT_AP_UNO_SRO (0 4)#define PTE_EXT_AP_UNO_SRW (PTE_EXT_AP0)#define PTE_EXT_AP_URO_SRW (PTE_EXT_AP1)#define PTE_EXT_AP_URW_SRW (PTE_EXT_AP1|PTE_EXT_AP0)#define PTE_EXT_TEX(x) (x) 6) /* v5 */#define PTE_EXT_APX (1 9) /* v6 */#define PTE_EXT_COHERENT (1 9) /* XScale3 */#define PTE_EXT_SHARED (1 10) /* v6 */#define PTE_EXT_NG (1 11) /* v6 */以上两种页表转换机制由CP15协处理器的控制寄存器c1中的bit23来选择。bit23为0时为第一种机制,否则为第二种。在CPU初始化后该位的默 认值为0。Linux在系统引导时会设置MMU的控制寄存器的相关位,其中把bit23设置为1,所以Linux在ARMv6体系架构上采用的是 ARMv6 MMU页表转换机制。 arch/arm/mm/proc-v6.S_v6_setup:. adr r5, v6_crval ldmia r5, r5, r6 mrc p15, 0, r0, c1, c0, 0 read control register bic r0, r0, r5 clear bits them orr r0, r0, r6 set them mov pc, lr return to head.S:_ret /* * V X F I D LR * . .E PUI. .T.T 4RVI ZFRS BLDP WCAM * rrrr rrrx xxx0 0101 xxxx xxxx x111 xxxx forced * 0 110 0011 1.00 .111 1101 we want */ .type v6_crval, #objectv6_crval: crval clear=0x01e0fb7f, mmuset=0x00c0387d, ucset=0x00c0187c注意到v6_crval定义了三个常量,首先mrc指令读取c1到r0,然后清除clear常量指定的比特位,然后设置mmuset指定的比特位,其中bit23为1。 在mov pc, lr跳转后将执行定义在head.S中的_enable_mmu函数,在进一步调节其它的比特位后最终将把r0中的值写回c1寄存器。 12.5.页面访问控制在谈到create_mapping之前,必须说明一下Linux是如何实现对页面的访问控制的。它定义了一个类型为struct mem_type的局部静态数组。根据不同的映射类型,它定义了不同的访问权限,它通过md参数中的type成员传递给create_mapping。 arch/arm/include/asm/io.h/* * Architecture ioremap implementation. */#define MT_DEVICE 0#define MT_DEVICE_NONSHARED 1#define MT_DEVICE_CACHED 2#define MT_DEVICE_WC 3arch/arm/include/asm/mach/map.h/* types 0-3 are defined in asm/io.h */#define MT_UNCACHED 4#define MT_CACHECLEAN 5#define MT_MINICLEAN 6#define MT_LOW_VECTORS 7#define MT_HIGH_VECTORS 8#define MT_MEMORY 9#define MT_ROM 10系统中定义了多个映射类型,最常用的是MT_MEMORY,它对应RAM;MT_DEVICE则对应了其他I/O设备,应用于 ioremap;MT_ROM对应于ROM;MT_LOW_VECTORS对应0地址开始的向量;MT_HIGH_VECTORS对应高地址开始的向量, 它有vector_base宏决定。 arch/arm/mm/mm.hstruct mem_type unsigned int prot_pte; unsigned int prot_l1; unsigned int prot_sect; unsigned int domain;尽管Linux在多数系统上实现或者模拟了3级页表,但是在ARM Linux上它只实现了主页表和两级页表。主页表通过ARM CPU的段表实现,段表中的每个页表项管理1M的内存,虚拟地址只需要一次转换既可以得到物理地址,它通常存放在swapper_pg_dir开始的 16K区域内。两级页表只有在被映射的物理内存块不满足1M的情况下才被使用,此时它由L1和L2组成。 prot_pte和prot_l1分别对应两级页表中的L2和L1,分别代表了页表项的访问控制位,其中prot_l1中还包含内存域 prot_sect代表主页表的访问控制位和内存域。 domain代表了内存域。在ARM处理器中,MMU将整个存储空间分成最多16个域,记作D0D15,每个域对应一定的存储区域,该区域具有相同的访问控制属性。arch/arm/include/asm/domain.h#define DOMAIN_KERNEL 0#define DOMAIN_TABLE 0#define DOMAIN_USER 1#define DOMAIN_IO 2ARM Linux 中只是用了16个域中的三个域D0-D2。它们由上面的宏来定义,在系统引导时初始化MMU的过程中将对这三个域设置域访问权限。以下是内存空间和域的对应表: 表21.内存空间和域的对应表内存空间域设备空间DOMAIN_IO内部高速SRAM空间/内部MINI Cache空间DOMAIN_KERNELRAM内存空间/ROM内存空间DOMAIN_KERNEL高低端中断向量空间DOMAIN_USER在ARM处理器中,MMU中的每个域的访问权限分别由CP15的C3寄存器中的两位来设定,c3寄存器的小为32bits,刚好可以设置16个域的访问权限。下表列出了域的访问控制字段不同取值及含义: 表22.ARM内存访问控制字值访问类型含义0b00无访问权限 此时访问该域将产生访问失效 0b01用户(client)根据CP15的C1控制寄存器中的R和S位以及页表中地址变换条目中的访问权限控制位AP来确定是否允许各种系统工作模式的存储访问 0b10保留使用该值会产生不可预知的结果0b11管理者(Manager)不考虑CP15的C1控制寄存器中的R和S位以及页表中地址变换条目中的访问权限控制位AP,在这种情况下不管系统工作在特权模式还是用户模式都不会产生访问失效Linux定义了其中可以使用的三种域控制: arch/arm/include/asm/domain.h#define DOMAIN_NOACCESS 0#define DOMAIN_CLIENT 1#define DOMAIN_MANAGER 3Linux在系统引导设置MMU时初始化c3寄存器来实现对内存域的访问控制。其中对DOMAIN_USER,DOMAIN_KERNEL和 DOMAIN_TABLE均设置DOMAIN_MANAGER权限;对DOMAIN_IO设置DOMAIN_CLIENT权限。如果此时读取c3寄存器, 它的值应该是0x1f。 arch/arm/include/asm/domain.h#define domain_val(dom,type) (type) cpu_domain; domain &= domain_val(dom, DOMAIN_MANAGER); thread-cpu_domain = domain | domain_val(dom, type); set_domain(thread-cpu_domain); while (0)访问权限由CP15的c1控制寄存器中的R和S位以及页表项中的访问权限控制位AP0:1以及访问权限扩展位APX来确定,通过R和S的组合控制方式在第一项中说明,并且已不被推荐使用。具体说明如下表所示。 表23.MMU中存储访问权限控制8APXAP1:0 特权模式访问权限 用户模式访问权限 0b00禁止访问;S=1,R=0或S=0,R=1时只读禁止访问;S=1,R=0时只读0b01读写禁止访问0b10读写只读0b11读写读写1b00保留保留1b01只读禁止访问1b10只读只读1b11只读只读8 参考ARM1176JZF-S Revision: r0p7-6.5.2 Access permissionsstatic struct mem_type mem_types = .MT_MEMORY = .prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE,.domain = DOMAIN_KERNEL,.;对于MT_MEMORY的内存映射类型,依据段页表的各位功能,定义了如下的宏,显然PMD_TYPE_SECT定义了段页表类型0b10,PMD_SECT_AP_WRITE和PMD_SECT_AP_READ则对应AP0和AP1访问权限控制位。根据表23 “MMU中存储访问权限控制”,在使用AP0:1进行权限控制时,CP15中的C1寄存器中的S和R标志位不影响权限,根据AP权限位的意义,并不能

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论