linux内核探索之内存管理(四):对页表和页表项的操作
接上一节,主要参考《深入Linux内核架构》(3.3节),即linux-3.18.3
1. 对PTE的操作
最后一级页表中的项不仅包含了指向页的内存位置的指针,还在上述的多于比特位包含了与页有关的附加信息。尽管这些数据是特定于CPU的,它们至少提供了有关页访问控制的一些信息。下列位在linux内核支持的大多数CPU中都可以找到。
arch/x86/include/asm/pgtable_types.h #define _PAGE_BIT_PRESENT 0 /* is present */ #define _PAGE_BIT_RW 1 /* writeable */ #define _PAGE_BIT_USER 2 /* userspace addressable */ #define _PAGE_BIT_PWT 3 /* page write through */ #define _PAGE_BIT_PCD 4 /* page cache disabled */ #define _PAGE_BIT_ACCESSED 5 /* was accessed (raised by CPU) */ #define _PAGE_BIT_DIRTY 6 /* was written to (raised by CPU) */ #define _PAGE_BIT_PSE 7 /* 4 MB (or 2MB) page */ #define _PAGE_BIT_PAT 7 /* on 4KB pages */ #define _PAGE_BIT_GLOBAL 8 /* Global TLB entry PPro+ */ #define _PAGE_BIT_SOFTW1 9 /* available for programmer */ #define _PAGE_BIT_SOFTW2 10 /* " */ #define _PAGE_BIT_SOFTW3 11 /* " */ #define _PAGE_BIT_PAT_LARGE 12 /* On 2MB or 1GB pages */ #define _PAGE_BIT_SPECIAL _PAGE_BIT_SOFTW1 #define _PAGE_BIT_CPA_TEST _PAGE_BIT_SOFTW1 #define _PAGE_BIT_SPLITTING _PAGE_BIT_SOFTW2 /* only valid on a PSE pmd */ #define _PAGE_BIT_HIDDEN _PAGE_BIT_SOFTW3 /* hidden by kmemcheck */ #define _PAGE_BIT_SOFT_DIRTY _PAGE_BIT_SOFTW3 /* software dirty tracking */ #define _PAGE_BIT_NX 63 /* No execute: only valid after cpuid check */ #define _PAGE_BIT_NUMA (_PAGE_BIT_GLOBAL+1) #define _PAGE_BIT_PROTNONE _PAGE_BIT_GLOBAL #define _PAGE_BIT_FILE _PAGE_BIT_DIRTY #define _PAGE_PRESENT (_AT(pteval_t, 1) << _PAGE_BIT_PRESENT) #define _PAGE_RW (_AT(pteval_t, 1) << _PAGE_BIT_RW) #define _PAGE_USER (_AT(pteval_t, 1) << _PAGE_BIT_USER) #define _PAGE_PWT (_AT(pteval_t, 1) << _PAGE_BIT_PWT) #define _PAGE_PCD (_AT(pteval_t, 1) << _PAGE_BIT_PCD) #define _PAGE_ACCESSED (_AT(pteval_t, 1) << _PAGE_BIT_ACCESSED) #define _PAGE_DIRTY (_AT(pteval_t, 1) << _PAGE_BIT_DIRTY) #define _PAGE_PSE (_AT(pteval_t, 1) << _PAGE_BIT_PSE) #define _PAGE_GLOBAL (_AT(pteval_t, 1) << _PAGE_BIT_GLOBAL) #define _PAGE_SOFTW1 (_AT(pteval_t, 1) << _PAGE_BIT_SOFTW1) #define _PAGE_SOFTW2 (_AT(pteval_t, 1) << _PAGE_BIT_SOFTW2) #define _PAGE_PAT (_AT(pteval_t, 1) << _PAGE_BIT_PAT) #define _PAGE_PAT_LARGE (_AT(pteval_t, 1) << _PAGE_BIT_PAT_LARGE) #define _PAGE_SPECIAL (_AT(pteval_t, 1) << _PAGE_BIT_SPECIAL) #define _PAGE_CPA_TEST (_AT(pteval_t, 1) << _PAGE_BIT_CPA_TEST) #define _PAGE_SPLITTING (_AT(pteval_t, 1) << _PAGE_BIT_SPLITTING)
_PAGE_PRESENT:指定了虚拟内存页是否存在于内存中。页可能换出到交换区。
_PAGE_ACCESSED:CPU每次访问页时,会自动设置_PAGE_ACCESSED。内核会定期检查该比特位,以确认页使用的活跃程度。不经常使用的页更适合换出。在读写访问之后设置该比特位。
_PAGE_DIRTY表示该页是否是“脏的”,即页的内容是否已经修改过。
_PAGE_FILE的数值与_PAGE_DIRTY相同,但用于不同的上下文,即页不在内存中的时候。不存在的页不可能是脏的,因此可以重新解释该比特位。如果没有设置,则该项指向一个换出页的位置。如果该项属于非线性文件映射,则需要设置_PAGE_FILE。
_PAGE_USER:是否允许用户空间代码访问。不设置只能在内核态访问。
_PAGE_READ、_PAGE_WRITE和_PAGE_EXECUTE指定了普通的用户进程是否允许读取、写入、执行该页中的及其代码。
内核内存中的页必须防止用户进程写入。但即使属于用户进程的页,页无法保证可以写入。对于访问权限粒度没有那么细的体系结构而言,如果没有进一步的准则可区分读写访问权限,则会定义_PAGE_RW常数,用于同事允许或禁止读写访问。
每种体系结构都必须提供使得内存管理子系统能够修改pte_t项中的额外的比特位,即保存额为的比特位的__pgprot数据类型,以及修改这些比特位的pte_modify函数。内核还定义了各种函数,用于查询和设置内存页与体系结构相关的状态。如下是常用的设置内存页的函数。
函数 |
描述 |
pte_present |
页是否在内存中 |
pte_read |
从用户空间是否可读 |
pte_write |
是否可以写入到该页 |
Pte_exec |
该页中的数据是否可以作为二进制执行 |
Pte_dirty |
页是否是脏的,其内容是否修改过 |
Pte_file |
该页表项属于非线性映射吗 |
Pte_young |
访问位设置好了没有(_PAGE_ACCESSED) |
Pte_rdprotect |
清除该页的读权限 |
Pte_wrprotect |
清除该页的写权限 |
Pte_exprotect |
清除执行该页中二进制数据的权限 |
Pte_mkread |
设置读权限 |
Pte_mkwrite |
设置写权限 |
Pte_mkexec |
允许执行页的内容 |
Pte_mkdirty |
将页标记为脏 |
Pte_mkclean |
“清除”页,通常是指消除_PAGE_DIRTY位 |
Pte_mkyoung |
设置访问位,在大多数体系结构上是_PAGE_ACCESSED |
Pte_mkold |
清除访问位 |
这些函数经常3个1组,分别用于设置、删除、查询某个特定的属性。内核假定页面数据的访问可以按3种不同的方式控制,即读、写和执行权限。
2. 页表项的创建和操作
函数 |
描述 |
Mk_pte |
创建一个页表项。必须将page实例和所需的页访问控制权限作为参数传递 |
Pte_page |
获得页表项描述的页对应的page实例地址 |
Pgd_alloc |
分配并初始化可容纳一个完整页表的内存 |
Pud_alloc |
|
Pmd_alloc |
|
Pte_alloc |
|
Pgd_free |
释放页表占据的内存 |
Pud_free |
|
Pmd_free |
|
Pte_free |
|
Set_pgd |
设置页表中某项的值 |
Set_pud |
|
Set_pmd |
|
Set_pte |