linux内核工程师 3.01节 linux内核页机制

1.    三种地址:逻辑地址,线性地址,物理地址。

三者的详细解释:

         逻辑地址(logical address):由段(segment(16)和偏移量(offsetdisplacement(32)组成。

         线性地址(linear address)(也称虚拟地址virtual address):是一个32位无符号整数,用来表示高达4GB的地址。

物理地址(physical address):实际地址。

 

 

分段目的:把逻辑地址转换为线性地址,如下图所示:

 

 

2.1 段标识符和段寄存器

 

        

段标识符(段选择符)(存放在段寄存器中)字段如下:

index

用来得到段描述符在GDTLDT中的偏移(位置)

TI

指明段描述符是在GDT(TI=0)或在LDT(TI=1)

RPL

请求特权级,

由于一个段描述符是8字节,因此它的地址=GDTLDT内的首地址+(index*8)。能够保存在GDT中的段描述符的最大数目是8191(213-1),其中13index的位数。

不同段的段描述符构成段描述符表,段描述符(8字节)存放在全局描述符表(GDT)或局部描述符表(LDT)中。GDT在主存中的地址和大小存放在gdtr控制寄存器中,当前正被使用的LDT地址和大小存放在ldtr控制寄存器中。

2.2 段描述符

 

CPL(Current Privilege Level)

当前特权级

DPL(Descriptor Privilege Level)

描述符特权级

RPL ( Require Privilege Level)

请求特权级,

Liunx中广泛采用的段描述符类型:代码段描述符、数据段描述符、任务状态段描述符、局部描述符表描述符(代表一个包含LDT的段)

 

 

2.3 快速访问段描述符:(是由逻辑地址得到段的一个流程)

 

 

1.4  Linux中的分段

用户代码段

__USER_CS

用户数据段

__USER_DS

内核代码段

__KERNEL_CS

内核数据段

__KERNEL_DS

1.5  Linux GDT

每个处理器中只有一个GDT,所以在多处理器系统中有多个GDT。一般的,每个处理器中的都是GDT的一个副本。除了以下的三种情况:

1.每个处理器都有自己的TSS段,因此其对应的GDT项不同。

2.GDT中只有少数项可能依赖于CPU正在执行的进程。

3.在某些情况下,处理器可能临时修改GDT副本中的某个项。

所有的GDT存放在cpu_gdt_table数组中,而所有的GDT的地址和它们的大小(初始化gdtr时用)存放在cpu_gdt_descr数组中。这些都在arch/i386/kernel/head.s中定义。

如下图所示,一个GDT中包含18个描述符和14个空的、未使用的、或保留的项。

 

 

1.6  Linux LDT

大多数用户态程序不使用LDT,这样内核就在GDT中定义了一个缺省的LDT来供大多数进程共享。缺省的LDT存放在default_ldt数组中。它含有5个项,内核只使用了两个项:用于iBCS执行文件的调用门和Solaris/x86可执行文件的调用门。调用门是一种机制:用于在调用预定义函数时改变CPU的特权级。

如果进程需要创建自己的LDT,则使用modify_ldt()系统调用。用户创建的LDT需要自己的段,此时GDT中的LDT表项相应的就要被修改。

2.    分页

目的:把线性地址转换为物理地址。其中的一个关键任务:比较所请求的访问类型与线性地址的访问权限。如果内存访问无效,则产生一个缺页异常。

页:以固定长度为单位的线性地址。页内部的线性地址被映射到连续的物理地址中。(即代表一组数据)

页框:RAM被分成固定长度的页框,每一个页框包含一个页。(即主存中的物理地址)

页表:把线性地址映射到物理地址的数据结构,它存放在主存中,并在启用分页单元之前由内核进行初始化。

所有的80x86都支持分页,它通过设置cr0寄存器的PG标志启用(PG=0)

3.1 常规分页

 

32位的线性地址被分为3个域(4KB的页)

重要的概率

组成

页目录

页目录项

页表

页表项

线性地址到物理地址的转换如下图所示:

 

 

3.2 扩展分页

 

3.3 物理地址扩展(PAE)分页

看书p56-57

3.4 硬件保护方案

段有三种存取权限(读、写、执行),页只有两种存取权限(读、写)

PAE(Physical Address Extension)

物理地址扩展

PSE(Page Size Extension)

页大小扩展

PGE(Page Global Enable)

页全局启用

3.5 硬件高速缓存

原因:CPURAM之间速度不匹配。

原理:基于局部性原理,用小而快的内存来存放最近最常使用的代码。以脉冲突发模式(burst mode)在慢速DRAM和快速片上SRAM之间传送行(一种新单位)

映射关系:直接映射、全相关、N-路相关。

硬件高速缓存单元的组成:一个硬件高速缓存内存、一个高速缓存控制器。Cache hitcache miss。通写(write-through)、回写(write-back)

 

多处理器高速缓存,需要保持高速缓存间的内容同步。就有一种高速缓存侦听(cache snooping)活动来实现同步。

 

Linux中对所有的页框都启用高速缓存,对于写操作总是采用回写策略。

3.    Linux中的分页

4.1 分页概述

Linux2.6.11开始采用四级分页模型:(为了适用于32位和64位系统)

l  PGD(Page Global Directory)

l  PUD(Page Upper Directory)

l  PMD(Page Middle Directory)

l  PT(Page Table)

PGD中包含若干PUD的地址,PUD中包含若干PMD的地址,PMD中又包含若干PT的地址。每一个页表项指向一个页框。线性地址被分为五个部分,如下图所示:(图中没有指明每一部分的大小,因为每一部分的大小与具体的计算机体系结构相关。)

 

没有启用物理地址扩展的32位系统,两级页表就可以了。Linux通过使PUDPMD对应的位全为0,使得每个PUDPMD都只有一个项。此时PGDPUDPMDPT中表项的个数分别是:1024111024

启用物理地址扩展的32位系统使用了三级页表。Linux中的PGD对应于80x86的页目录指针表(PDPT)PUD对应位全为0PMD对应于80x86的页目录,PT对应于80x86的页表。此时PGDPUDPMDPT中表项的个数分别是:41512512

Linux的进程处理依赖于分页:

1.       给每一个进程分配一块不同的物理地址空间,可以有效地防止寻址错误。

2.       区别页和页框之不同。这就允许存放在某个页框中的一个页,然后保存到磁盘上,以后重新装入这同一页时又可以被装在不同的页框中。这是虚拟内存机制的基本要素。

当发生进程切换时,linuxcr3控制寄存器的内容保存在前一个执行进程的描述符中,然后把下一个要执行进程的描述符的值装入cr3寄存器中,因此,当新进程重新开始在CPU上执行时,分页单元指向一组正确的页表。

 

4.2 常见宏和函数

1. 页相关:PAGE_SIZE使用PAGE_SHIFT返回页大小,PAGE_MASK(0xfffff000)用来屏蔽PAGE_SHIFT(Offset字段)。PAGE_SHIFT=log2PAGE_SIZE,所以PAGE_SHIFT的值一般为12。212=4096

2. PMD相关:PMD_SIZE使用PMD_SHIFT返回页中间目录项可映射区域大小。PMD_MASK用来屏蔽Offset字段和Table字段的所有位。

当PAE被禁用时,PMD_SHIFT的值为22,PMD_SIZE为222或4M,PMD_MASK(0xffc00000)。

当PAE被激活时,PMD_SHIFT的值为21,PMD_SIZE为221或2M,PMD_MASK(0xffe00000)。

当使用扩展分页时,LARGE_PAGE_SIZE=PMD_SIZE,LARGE_PAGE_MASK=PMD_MASK。

3. PUD相关:PUD_SIZE使用PUD_SHIFT返回页上级目录项映射的区域大小,PUD_MASK用来屏蔽Offset字段、Table字段和Midddle Air字段的所有位。在80x86处理器上PUD_SHIFT=PMD_SHIFT,PUD_SIZE=PMD_SIZE。

4. PGD相关:PGDIR_SIZE使用PGD_SHIFT返回页全局目录项映射的区域大小,PGD_MASK用来屏蔽Offset字段、Table字段、Midddle Air字段和Upper Air字段的所有位。

当PAE被禁止时,PGDIR_SHIFT=22=PMD_SHIFT=PUD_SHIFT,PGDIR_SIZE=222=4M,PGDIR_MASK(0xffc00000)。

当PAE被激活时,PGDIR_SHIFT=30,PGDIR_SIZE=230=1GB,PGDIR_MASK(0xc0000000)

 

对页表项操作的宏

 

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 Chapter 9) 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 "Kernel Mappings of High-Memory Page Frames" in Chapter 8), 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 "Kernel Page Tables").

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.

 

4.3 物理内存布局

内核必须明确那些物理地址对内核可用而那些不可用。

内核将页框记为保留(保留页框中的页绝不能被动态分配或交换到磁盘上)

1.       在不可用的物理地址范围内的页框。

2.       含有内核代码和已初始化的数据结构的页框。

Linux内核安装在RAM中从物理地址0x0010000开始的地方,也就是说,从第二个MB开始。为什么不从第一个MB开始呢?解释如下:

1.       页框0BIOS使用,存放加电自检(POST Power-On Self-Test)期间检查到的系统硬件配置。

2.       物理地址从0x000a00000x000fffff的范围通常留给BIOS例程,并且映射到ISA图形卡上的内部内存。这个区域就是从640KB1MB之间著名的洞:物理地址存在但被保留,对应的页框不能由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并恰当地建立分页表。

猜你喜欢

转载自blog.csdn.net/zjy900507/article/details/80676436