mirror of
https://github.com/gatieme/LDD-LinuxDeviceDrivers.git
synced 2026-08-20 22:04:56 +08:00
更新了内存管理之内存描述(二)--内存结点pg_data_t详解...
This commit is contained in:
@@ -56,6 +56,17 @@ Linux把物理内存划分为三个层次来管理
|
||||
-------
|
||||
|
||||
|
||||
分页单元可以实现把线性地址转换为物理地址, 为了效率起见, 线性地址被分为固定长度为单位的组, 称为"页", 页内部的线性地址被映射到连续的物理地址. 这样内核可以指定一个页的物理地址和其存储权限, 而不用指定页所包含的全部线性地址的存储权限.
|
||||
|
||||
分页单元把所有RAM分为固定长度的页帧(也叫页框, 物理页, 英文page frame). 每一个页帧包含一个页(page). 也就是说一个页帧的长度与一个页的长度一致. 页框是主存的一部分, 因此也是一个存储区域. 简单来说, 页是一个数据块, 可以存放在任何页框(内存中)或者磁盘(被交换至交换分区)中
|
||||
|
||||
我们今天就来详细讲解一下linux下物理页帧的描述
|
||||
|
||||
#2 页帧
|
||||
-------
|
||||
|
||||
内核把物理页作为内存管理的基本单位. 尽管处理器的最小可寻址单位通常是字, 但是, 内存管理单元MMU通常以页为单位进行处理. 因此,从虚拟内存的上来看,页就是最小单位.
|
||||
|
||||
页帧代表了系统内存的最小单位, 对内存中的每个页都会创建struct page的一个实例. 内核必须要保证page结构体足够的小,否则仅struct page就要占用大量的内存.
|
||||
|
||||
因为即使在中等程序的内存配置下, 系统的内存同样会分解为大量的页. 例如, IA-32系统中标准页长度为4KB, 在内存大小为384MB时, 大约有100000页. 就当今的标准而言, 这个容量算不上很大, 但页的数目已经非常可观了
|
||||
@@ -65,13 +76,6 @@ Linux把物理内存划分为三个层次来管理
|
||||
页的广泛使用, 增加了保持结构长度的难度 : 内存管理的许多部分都使用页, 用于各种不同的用途. 内核的一部分可能完全依赖于struct page提供的特定信息, 而这部分信息堆内核的其他部分页可能是完全无用的. 等等.
|
||||
|
||||
|
||||
我们今天就来详细讲解一下linux下物理页帧的描述
|
||||
|
||||
#2 页帧
|
||||
-------
|
||||
|
||||
内核把物理页作为内存管理的基本单位. 尽管处理器的最小可寻址单位通常是字, 但是, 内存管理单元MMU通常以页为单位进行处理. 因此,从虚拟内存的上来看,页就是最小单位.
|
||||
|
||||
##2.1 struct page结构
|
||||
-------
|
||||
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
服务器体系与共享存储器架构
|
||||
=======
|
||||
|
||||
| 日期 | 内核版本 | 架构| 作者 | GitHub| CSDN |
|
||||
| ------- |:-------:|:-------:|:-------:|:-------:|:-------:|
|
||||
| 2016-06-14 | [Linux-4.7](http://lxr.free-electrons.com/source/?v=4.7) | X86 & arm | [gatieme](http://blog.csdn.net/gatieme) | [LinuxDeviceDrivers](https://github.com/gatieme/LDD-LinuxDeviceDrivers) | [Linux内存管理](http://blog.csdn.net/gatieme/article/category/6225543) |
|
||||
|
||||
|
||||
|
||||
|
||||
#1 分页机制
|
||||
-------
|
||||
|
||||
##1.1 为什么使用多级页表来完成映射
|
||||
-------
|
||||
|
||||
|
||||
|
||||
<font color=0x00ffff>
|
||||
但是为什么要使用多级页表来完成映射呢?
|
||||
</font>
|
||||
|
||||
用来将虚拟地址映射到物理地址的数据结构称为**页表**, 实现两个地址空间的关联最容易的方式是使用数组, 对虚拟地址空间中的每一页, 都分配一个数组项. 该数组指向与之关联的**页帧**, 但这会引发一个问题, 例如, IA-32体系结构使用4KB大小的页, 在虚拟地址空间为4GB的前提下, 则需要包含100万项的页表. 这个问题在64位体系结构下, 情况会更加糟糕. 而每个进程都需要自身的页表, 这回导致系统中大量的所有内存都用来保存页表.
|
||||
|
||||
因为虚拟地址空间的大部分们区域都没有使用, 因而页没有关联到页帧, 那么就可以使用功能相同但内存用量少的多的模型: **多级页表**
|
||||
|
||||
为减少页表的大小并容许忽略不需要的区域, 计算机体系结构的涉及会将虚拟地址分成多个部分.
|
||||
|
||||
|
||||
|
||||
|
||||
<font color=0x00ffff>
|
||||
但是新的问题来了, 到底采用几级页表合适呢?
|
||||
</font>
|
||||
|
||||
##1.2 32位系统中2级页表
|
||||
-------
|
||||
|
||||
|
||||
从80386开始, intel处理器的分页单元是4KB的页, 32位的地址空间被分为3部分
|
||||
|
||||
| 单元 | 描述 |
|
||||
|:---:|:----:|
|
||||
| 页目录表Directory | 最高10位 |
|
||||
| 页中间表Table | 中间10位 |
|
||||
| 页内偏移 | 最低12位 |
|
||||
|
||||
即页表被划分为页目录表Directory和页中间表Tabl两个部分
|
||||
|
||||
此种情况下, 线性地址的转换分为两步完成.
|
||||
|
||||
* 第一步, 基于两级转换表(页目录表和页中间表), 最终查找到地址所在的页帧
|
||||
|
||||
* 第二步, 基于偏移, 在所在的页帧中查找到对应偏移的物理地址
|
||||
|
||||
使用这种二级页表可以有效的减少每个进程页表所需的RAM的数量. 如果使用简单的一级页表, 那将需要高达$2^20$个页表, 假设每项4B, 则共需要占用$2^20 * 4B = 4MB$的RAM来表示每个进程的页表. 当然我们并不需要映射所有的线性地址空间(32位机器上线性地址空间为4GB), 内核通常只为进程实际使用的那些虚拟内存区请求页表来减少内存使用量.
|
||||
|
||||
##1.3 64位系统中的分页
|
||||
-------
|
||||
|
||||
正常来说, 对于32位的系统两级页表已经足够了, 但是对于64位系统的计算机, 这远远不够.
|
||||
|
||||
首先假设一个大小为4KB的标准页. 因为1KB覆盖$2^10$个地址的范围, 4KB覆盖$2^12$个地址, 所以offset字段需要12位.
|
||||
|
||||
这样线性地址空间就剩下64-12=52位分配给页中间表Table和页目录表Directory. 如果我们现在决定仅仅使用64位中的48位来寻址(这个限制其实已经足够了, 2^48=256TB, 即可达到256TB的寻址空间). 剩下的48-12=36位被分配给Table和Directory字段. 即使我们现在决定位两个字段各预留18位, 那么每个进程的页目录和页表都包含$2^18$个项, 即超过256000个项.
|
||||
|
||||
基于这个原因, 所有64位处理器的硬件分页系统都使用了额外的分页级别. 使用的级别取决于处理器的类型
|
||||
|
||||
| 平台名称 | 页大小 | 寻址所使用的位数 | 分页级别数 | 线性地址分级 |
|
||||
|:---:|:---:|:---:|:---:|:---:|
|
||||
| alpha | 8KB | 43 | 3 | 10 + 10 + 10 + 13 |
|
||||
| ia64 | 4KB | 39 | 3 | 9 + 9 + 9 + 12 |
|
||||
| ppc64 | 4KB | 41 | 3 | 10 + 10 + 9 + 12 |
|
||||
| sh64 | 4KB | 41 | 3 | 10 + 10 + 9 + 12 |
|
||||
| x86_64 | 4KB | 48 | 4 | 9 + 9 + 9 + 9 + 12 |
|
||||
|
||||
|
||||
|
||||
##1.4 Linux中的分页
|
||||
-------
|
||||
|
||||
层次话的页表用于支持对大地址空间快速, 高效的管理. 因此linux内核堆页表进行了分级.
|
||||
|
||||
前面我们提到过, 对于32位系统中, 两级页表已经足够了. 但是64位修奥更多数量的分页级别.
|
||||
|
||||
为了同时支持适用于32位和64位的系统, Linux采用了通用的分页模型. 在Linux-2.6.10版本中, Linux采用了三级分页模型. 而从2.6.11开始普遍采用了四级分页模型.
|
||||
|
||||
目前的内核的内存管理总是嘉定使用四级页表, 而不管底层处理器是否如此.
|
||||
|
||||
| 单元 | 描述 |
|
||||
|:---:|:----:|
|
||||
| 页全局目录 | Page GlobalDirectory |
|
||||
| 页上级目录 | Page Upper Directory |
|
||||
| 页中间目录 | Page Middle Directory |
|
||||
| 页表 | Page Table |
|
||||
| 页内偏移 | Page Offset |
|
||||
|
||||
|
||||
Linux不同于其他的操作系统, 它把计算机分成独立层(体系结构无关)/依赖层(体系结构相关)两个层次. 对于页面的映射和管理也是如此. 页表管理分为两个部分, 第一个部分依赖于体系结构, 第二个部分是体系结构无关的. 所有数据结构几乎都定义在特定体系结构的文件中. 这些数据结构的定义可以在头文件`arch/对应体系/include/asm/page.h`
|
||||
和`arch/对应体系/include/asm/pgtable.h`中找到. 但是对于AMD64和IA-32已经统一为一个体系结构. 但是在处理页表方面仍然有很多的区别, 因为相关的定义分为两个不同的文件`arch/x86/include/asm/page_32.h`
|
||||
和`arch/x86/include/asm/page_64.h`
|
||||
, 类似的也有`pgtable_xx.h` .
|
||||
|
||||
|
||||
|
||||
#页表
|
||||
-------
|
||||
|
||||
|
||||
Linux内核通过四级页表将虚拟内存空间分为5个部分(4个页表项用于选择页, 1个索引用来表示页内的偏移). 各个体系结构不仅地址长度不同, 而且地址字拆分的方式也不一定相同. 因此内核使用了宏用于将地址分解为各个分量.
|
||||
http://bbs.csdn.net/topics/390831818
|
||||
|
||||
http://blog.csdn.net/hmsiwtv/article/details/39956981
|
||||
|
||||
http://biancheng.dnbcw.info/linux/335152.html
|
||||
|
||||
http://blog.chinaunix.net/uid-21718047-id-3140041.html
|
||||
|
||||
http://blog.csdn.net/gatieme/article/details/50651561
|
||||
|
||||
http://blog.csdn.net/gatieme/article/details/50756050
|
||||
|
||||
http://www.360doc.com/content/11/0804/10/7204565_137844381.shtml
|
||||
|
||||
Reference in New Issue
Block a user