linux为啥要采用三级页表?该机制如何工作

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux为啥要采用三级页表?该机制如何工作相关的知识,希望对你有一定的参考价值。

  Linux启动并建立一套完整的页表机制要经过以下几个步骤:
  1.临时内核页表的初始化(setup_32.s)
  2.启动分页机制(head_32.s)
  3.建立低端内存和高端内存固定映射区的页表( init_memory_mapping())
  4.建立高端内存永久映射区的页表并获取固定映射区的临时映射区页表(paging_init())
  具体分析低端内存页表的建立
  在setup_arch()中内核通过调用init_memory_mapping()来建立低端内存页表
  [cpp] view plaincopy
  void __init setup_arch(char **cmdline_p)
  ...
  ...
  /* max_pfn_mapped is updated here */
  max_low_pfn_mapped = init_memory_mapping(0, max_low_pfn<<PAGE_SHIFT);
  max_pfn_mapped = max_low_pfn_mapped;
  ...
  ...
  内核将低端内存的起始地址(0),和低端内存的结束地址(max_low_pfn<<PAGE_SHIFT)传递给init_memory_mapping(),下面来看Init_memory_mapping()的具体实现,简单起见,只分析32位系统的情况。
参考技术A Linux启动并建立一套完整的页表机制要经过以下几个步骤:

1.临时内核页表的初始化(setup_32.s)

2.启动分页机制(head_32.s)

3.建立低端内存和高端内存固定映射区的页表( init_memory_mapping())

4.建立高端内存永久映射区的页表并获取固定映射区的临时映射区页表(paging_init())

下面主要介绍3和4

一、低端内存页表的建立
在setup_arch()中内核通过调用init_memory_mapping()来建立低端内存页表
[cpp] view plaincopy
void __init setup_arch(char **cmdline_p)

...
...
/* max_pfn_mapped is updated here */
max_low_pfn_mapped = init_memory_mapping(0, max_low_pfn<<PAGE_SHIFT);
max_pfn_mapped = max_low_pfn_mapped;
...
...
内核将低端内存的起始地址(0),和低端内存的结束地址(max_low_pfn<<PAGE_SHIFT)传递给init_memory_mapping(),下面来看Init_memory_mapping()的具体实现,简单起见,只分析32位系统的情况本回答被提问者和网友采纳

02 linux011内存管理

CR0.PG=1将开启内存页机制,由CR3保存内存页机制相关数据结构体的内存地址。

1 页表目录

页表目录用于索引页表,在页表目录中用于索引页表的信息被(此文)称为页表目录项。

+---------------------+
|page table dir item 0|
+---------------------+
|page table dir item 1|
+---------------------+
|         ...         |

页表目录项

|31                   12|11 9               0
+-----------------------+---+--+-+-+--+-+-+-+
|                       |   |  | | |  |U|R| |
|page table addr[31..12]|AVL|00|D|A|00|/|/|P|
|                       |   |  | | |  |S|W| |
+-----------------------+---+--+-+-+--+-+-+-+

页表地址:4Kb对齐,低12位由CPU自动补0;

D/A: 已修改/访问位,由硬件置位;

R/W: 1 - 可读写;0 - 仅可读或执行;

U/S: 特权级;

P: 1 - 页表可用;0 - 页表不可用。

2 页表

页表用于索引一块内存页,用于索引内存页的信息被(此文)成为页表项。

+-----------------+
|page table item 0|
+-----------------+
|page table item 1|
+-----------------+
|       ...       |

页表项的位格式同页表目录项。

3 页机制所管理的内存大小

在32位地址下,1个页表目录大小为4Kib,可以索引1024个页表;每个页表大小为4Kib可以描述1024个内存页,每个内存页大小为4Kb;则1个页表目录最大可描述4Gib内存。

4 页映射

对于任意一个32位逻辑地址,可根据页机制映射到一个物理内存地址

|31                22|21              12|11             0|
----------------------------------------------------------
|       DT_IDX       |      DTI_IDX     |     OFFSET     |
----------------------------------------------------------

DT_IDX: 页表目录项在页目录中的索引;
DTI_IDX: 页表项在页表中的索引;
OFFSET: 在内存页内的偏移。

如 0x00000000 将用索引到页目录中的第1个(0)页目录项,根据该页目录项索引到对应的页表,在该页表中将索引到第1个(0)页表项,从该页表项中可得到内存页的物理地址mem,最后映射到的内存地址为mem(偏移为0)。

5 管理物理内存分配的数组

(页表中的)逻辑地址所映射的物理地址依据 mem_map 数组来分配。

/* mem_map数组以页为单位标识物理内存的使用情况。
 * mem_map element        memory space
 *  mem_map[0]        [0x100000, 0x100fff]
 *  mem_map[1]        [0x101000, 0x101fff]
 *      ...                   ...        
 *  mem_map[3838]     [0xffe000, 0xffefff]
 *  mem_map[3839]     [0xfff000, 0xffffff]
 * 
 * mem_map[i] = count 标识
 * 内存段[0x100000 + i << 12, 0x100000 + i << 12 + 0xfff]
 * 的引用计数为count, i = [0..PAGING_PAGES - 1]。*/
static unsigned char mem_map [ PAGING_PAGES ] = 0,;

6 写时拷贝

页机制可以让多个不同逻辑地址指向同一物理内存页——在对应页表项中填写相同的物理内存页起始地址。

在创建进程时,不同进程可以拥有不同的逻辑地址空间,但共享父进程的物理内存页。当有进程要更改共享的物理内存页时,就在该进程的页表项中为其重新映射一块空闲的物理内存页,并将原物理内存页的数据拷贝到新物理内存页中,再实施更改操作。这就是页机制支持的写时拷贝。

7 linux0.11 物理内存划分

+-------------+--------+------------+-------------+
| OS routines | BUFFER | [RAM-DISK] | MAIN_MEMORY |
+-------------+--------+------------+-------------+
0x0           end      4Mb                        16Mb

BUFFER: 用作硬盘、软盘等外设缓冲区;

RAM-DISK: 用作虚拟磁盘(若定义);

MAIN_MEMORY: 为剩余内存,将用作内核数据结构体和进程运行的内存空间。

以上是关于linux为啥要采用三级页表?该机制如何工作的主要内容,如果未能解决你的问题,请参考以下文章

Linux分页机制之概述--Linux内存管理

linux kernel 内存管理-页表、TLB

如何更改页表条目以在 linux 中引发页面错误?

在“分叉”一个进程时,为啥 Linux 内核会为每个新创建的进程复制内核页表的内容?

arm-linux内存管理学习笔记-内核临时页表的建立

02 linux011内存管理