全部博文(77)
分类:
2012-08-07 16:58:45
原文地址:《深入理解Linux内核》内存寻址学习心得3 作者:lsy011025
对页表项操作的宏
Macro name |
Description |
pgd_index(addr) |
Yields the index (relative position) of the entry in the Page Global Directory that maps the linear address addr. |
pgd_offset(mm, addr) |
Receives as parameters the address of a memory descriptor cw (see ) and a linear address addr. The macro yields the linear address of the entry in a Page Global Directory that corresponds to the address addr; the Page Global Directory is found through a pointer within the memory descriptor. |
pgd_offset_k(addr) |
Yields the linear address of the entry in the master kernel Page Global Directory that corresponds to the address addr |
pgd_page(pgd) |
Yields the page descriptor address of the page frame containing the Page Upper Directory referred to by the Page Global Directory entry pgd. In a two- or three-level paging system, this macro is equivalent to pud_page() applied to the folded Page Upper Directory entry. |
pud_offset(pgd, addr) |
Receives as parameters a pointer pgd to a Page Global Directory entry and a linear address addr. The macro yields the linear address of the entry in a Page Upper Directory that corresponds to addr. In a two- or three-level paging system, this macro yields pgd, the address of a Page Global Directory entry. |
pud_page(pud) |
Yields the linear address of the Page Middle Directory referred to by the Page Upper Directory entry pud. In a two-level paging system, this macro is equivalent to pmd_page() applied to the folded Page Middle Directory entry. |
pmd_index(addr) |
Yields the index (relative position) of the entry in the Page Middle Directory that maps the linear address addr. |
pmd_offset(pud, addr) |
Receives as parameters a pointer pud to a Page Upper Directory entry and a linear address addr. The macro yields the address of the entry in a Page Middle Directory that corresponds to addr. In a two-level paging system, it yields pud, the address of a Page Global Directory entry. |
pmd_page(pmd) |
Yields the page descriptor address of the Page Table referred to by the Page Middle Directory entry pmd. In a two-level paging system, pmd is actually an entry of a Page Global Directory. |
mk_pte(p,prot) |
Receives as parameters the address of a page descriptor p and a group of access rights prot, and builds the corresponding Page Table entry. |
pte_index(addr) |
Yields the index (relative position) of the entry in the Page Table that maps the linear address addr. |
pte_offset_kernel(dir, addr) |
Yields the linear address of the Page Table that corresponds to the linear address addr mapped by the Page Middle Directory dir. Used only on the master kernel page tables. |
pte_offset_map(dir, addr) |
Receives as parameters a pointer dir to a Page Middle Directory entry and a linear address addr; it yields the linear address of the entry in the Page Table that corresponds to the linear address addr. If the Page Table is kept in high memory, the kernel establishes a temporary kernel mapping, to be released by means of pte_unmap. The macros pte_offset_map_nested and pte_unmap_nested are identical, but they use a different temporary kernel mapping. |
pte_page(x) |
Returns the page descriptor address of the page referenced by the Page Table entry x. |
pte_to_pgoff(pte) |
Extracts from the content pte of a Page Table entry the file offset corresponding to a page belonging to a non-linear file memory mapping. |
pgoff_to_pte(offset ) |
Sets up the content of a Page Table entry for a page belonging to a non-linear file memory mapping. |
使用两级页表时,一个页中间目录项仅含有一个指向下属页表的目录项,所以,页中间目录项只是页全局目录中的一项而已(????)。然而当处理页表时,创建一个页表项可能很复杂,因为包含页表项的那个页表可能就不存在。此时,有必要分配一个新页框,把它填为0,并把这个表项加入。
如果PAE被激活,内核使用三级页表。当内核创建一个新的页全局目录时,同时页分配四个相应的页中间目录;只有当父页全局目录被释放时,这四个页中间目录才得以释放。
当使用两级或三级分页时,页上级目录项总是被映射为页全局目录的一个单独项。
页分配函数
Function name |
Description |
pgd_alloc(mm) |
Allocates a new Page Global Directory; if PAE is enabled, it also allocates the three children Page Middle Directories that map the User Mode linear addresses. The argument mm (the address of a memory descriptor) is ignored on the 80 x 86 architecture. |
pgd_free( pgd) |
Releases the Page Global Directory at address pgd; if PAE is enabled, it also releases the three Page Middle Directories that map the User Mode linear addresses. |
pud_alloc(mm, pgd, addr) |
In a two- or three-level paging system, this function does nothing: it simply returns the linear address of the Page Global Directory entry pgd. |
pud_free(x) |
In a two- or three-level paging system, this macro does nothing. |
pmd_alloc(mm, pud, addr) |
Defined so generic three-level paging systems can allocate a new Page Middle Directory for the linear address addr. If PAE is not enabled, the function simply returns the input parameter pud that is, the address of the entry in the Page Global Directory. If PAE is enabled, the function returns the linear address of the Page Middle Directory entry that maps the linear address addr. The argument cw is ignored. |
pmd_free(x) |
Does nothing, because Page Middle Directories are allocated and deallocated together with their parent Page Global Directory. |
pte_alloc_map(mm, pmd, addr) |
Receives as parameters the address of a Page Middle Directory entry pmd and a linear address addr, and returns the address of the Page Table entry corresponding to addr. If the Page Middle Directory entry is null, the function allocates a new Page Table by invoking pte_alloc_one( ). If a new Page Table is allocated, the entry corresponding to addr is initialized and the User/Supervisor flag is set. If the Page Table is kept in high memory, the kernel establishes a temporary kernel mapping (see the section "" in ), to be released by pte_unmap. |
pte_alloc_kernel(mm, pmd, addr) |
If the Page Middle Directory entry pmd associated with the address addr is null, the function allocates a new Page Table. It then returns the linear address of the Page Table entry associated with addr. Used only for master kernel page tables (see the later section ""). |
pte_free(pte) |
Releases the Page Table associated with the pte page descriptor pointer. |
pte_free_kernel(pte) |
Equivalent to pte_free( ), but used for master kernel page tables. |
clear_page_range(mmu, start,end) |
Clears the contents of the page tables of a process from linear address start to end by iteratively releasing its Page Tables and clearing the Page Middle Directory entries. |
内核必须明确那些物理地址对内核可用而那些不可用。
内核将页框记为保留(保留页框中的页绝不能被动态分配或交换到磁盘上):
1. 在不可用的物理地址范围内的页框。
2. 含有内核代码和已初始化的数据结构的页框。
Linux内核安装在RAM中从物理地址0x0010000开始的地方,也就是说,从第二个MB开始。为什么不从第一个MB开始呢?解释如下:
1. 页框0有BIOS使用,存放加电自检(POST Power-On Self-Test)期间检查到的系统硬件配置。
2. 物理地址从0x000a0000到0x000fffff的范围通常留给BIOS例程,并且映射到ISA图形卡上的内部内存。这个区域就是从640KB到1MB之间著名的洞:物理地址存在但被保留,对应的页框不能由OS使用。
3. 第一个MB内的其它页框可能由特定计算机模型保留。
内核执行machine_specific_memory_setup()函数来建立物理地址映射,然后此函数调用setup_memory()函数去分析物理内存区域表并初始化一些变量来描述内核的物理内存布局。
描述内核物理内存布局的变量
Variable name |
Description |
num_physpages |
Page frame number of the highest usable page frame |
totalram_pages |
Total number of usable page frames |
min_low_pfn |
Page frame number of the first usable page frame after the kernel image in RAM |
max_pfn |
Page frame number of the last usable page frame |
max_low_pfn |
Page frame number of the last page frame directly mapped by the kernel (low memory) |
totalhigh_pages |
Total number of page frames not directly mapped by the kernel (high memory) |
highstart_pfn |
Page frame number of the first page frame not directly mapped by the kernel |
highend_pfn |
Page frame number of the last page frame not directly mapped by the kernel |
内核数据分为两组:初始化过的数据(_etext到_edata)和未初始化的数据(_edata到_end)。
进程页表:
The linear address space of a process is divided into two parts:
1. Linear addresses from 0x00000000 to 0xbfffffff can be addressed when the process runs in either User or Kernel Mode.
2. Linear addresses from 0xc0000000 to 0xffffffff can be addressed only when the process runs in Kernel Mode.
宏PAGE_OFFSET产生的值是0xc00000000,这就是进程在线性地址空间中的偏移量也是内核生存空间的开始。
内核维持一组自己使用的页表,放在主内核页全局目录中。系统初始化后,这组页表从未被任何进程(或内核线程)直接使用;主内核也全集目录的最高目录项部分作为参考模型,为系统中每个普通进程对应的页全局目录项提供参考模型。
内核映像刚被装入内存时,CPU任然运行于实模式,所以分页功能没有启用。
内核初始化自己页表的步骤:
1. 内核创建一个有限的地址空间,用来存放内核的代码段、数据段、初始页表、用于存放动态数据结构的等等,最少要128KB。
2. 内核充分利用剩余的RAM并恰当地建立分页表。