完善了README.md...

This commit is contained in:
gatieme
2016-08-05 11:47:17 +08:00
parent e8295dd2ea
commit dbf9e337b9
6 changed files with 1491 additions and 2 deletions
@@ -834,3 +834,38 @@ x86的系统中, find_max_pfn函数通过读取e820表获得最高的page frame
min_low_pfn max_pfn和max_low_pfn这3个值,也要用于对高端内存(high memory)的起止位置的计算。在arch/i386/mm/init.c文件中会对类似的highstart_pfn和highend_pfn变量作初始化。这些变量用于对高端内存页面的分配。后面将描述。
#
-------
在linux中,内核也不是对所有物理内存都一视同仁,内核而是把页分为不同的区, 使用区来对具有相似特性的页进行分组.
Linux必须处理如下两种硬件存在缺陷而引起的内存寻址问题:
1. 一些硬件只能用某些特定的内存地址来执行DMA
2. 一些体系结构其内存的物理寻址范围比虚拟寻址范围大的多。这样,就有一些内存不能永久地映射在内核空间上。
为了解决这些制约条件,Linux使用了三种区:
1. ZONE_DMA : 这个区包含的页用来执行DMA操作。
2. ZONE_NOMAL : 这个区包含的都是能正常映射的页。
3. ZONE_HIGHEM : 这个区包"高端内存",其中的页能不永久地映射到内核地址空间
而为了兼容一些设备的热插拔支持以及内存碎片化的处理, 内核也引入一些逻辑上的内存区.
1. ZONE_MOVABLE : 内核定义了一个伪内存域ZONE_MOVABLE, 在防止物理内存碎片的机制memory migration中需要使用该内存域. 供防止物理内存碎片的极致使用
2. ZONE_DEVICE : 为支持热插拔设备而分配的Non Volatile Memory非易失性内存
区的实际使用与体系结构是相关的。linux把系统的内存结点划分区, 一个区包含了若干个内存页面, 形成不同的内存池,这样就可以根据用途进行分配了
需要说明的是,区的划分没有任何物理意义, 只不过是内核为了管理页而采取的一种逻辑上的分组. 尽管某些分配可能需要从特定的区中获得页, 但这并不是说, 某种用途的内存一定要从对应的区来获取,如果这种可供分配的资源不够用了,内核就会占用其他可用去的内存.
下表给出每个区及其在X86上所占的列表
![每个区及其在X86上所占的列表](./images/zone_x86_32.png)
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

@@ -6,7 +6,8 @@
| 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) |
http://blog.chinaunix.net/uid-30282771-id-5176971.html
http://www.cnblogs.com/hanyan225/archive/2011/07/28/2119628.html
#1 前景回顾
-------
@@ -66,9 +67,122 @@ Linux把物理内存划分为三个层次来管理
我们今天就来详细讲解一下linux下物理页帧的描述
#2 页帧
-------
内核把物理页作为内存管理的基本单位. 尽管处理器的最小可寻址单位通常是字, 但是, 内存管理单元MMU通常以页为单位进行处理. 因此,从虚拟内存的上来看,页就是最小单位. 内核用[struct page(include/linux/mm_types.h?v=4.7, line 45)](http://lxr.free-electrons.com/source/include/linux/mm_types.h?v4.7#L45)结构表示系统中的每个物理页.
出于节省内存的考虑,struct page中使用了大量的联合体union.
##2.1 struct page结构
-------
| 字段 | 描述 |
|:---:|:----:|
| flag | 用来存放页的状态,每一位代表一种状态,所以至少可以同时表示出32中不同的状态,这些状态定义在linux/page-flags.h中 |
| count | 记录了该页被引用了多少次 |
| mapping | 指向与该页相关的address_space对象
| virtual | 页的虚拟地址,它就是页在虚拟内存中的地址。要理解的一点是page结构与物理页相关,而并非与虚拟页相关。因此,该结构对页的描述是短暂的。内核仅仅用这个结构来描述当前时刻在相关的物理页中存放的东西。这种数据结构的目的在于描述物理内存本身,而不是描述包含在其中的数据 |
|
#页面的状态flags
-------
page->flags描述page的状态和其他信息
```cpp
struct page {
/* First double word block */
unsigned long flags; /* Atomic flags,
some possibly updated asynchronously, 描述page的状态和其他信息 */
```
如下如所示
![page的flags标识](./images/flags.png)
主要分为4部分,其中标志位flag向高位增长, 其余位字段向低位增长,中间存在空闲位
| 字段 | 描述 |
|:----:|:---:|
| section | 主要用于稀疏内存模型SPARSEMEM,可忽略 |
| node | NUMA节点号, 标识该page属于哪一个节点 |
| zone | 内存域标志,标识该page属于哪一个zone |
| flag | page的状态标识 |
其中最后一个flag用于标识page的状态, 这些状态由枚举常量[`enum pageflags`](http://lxr.free-electrons.com/source/include/linux/page-flags.h?v=4.7#L74)定义, 定义在[include/linux/page-flags.h?v=4.7, line 74](http://lxr.free-electrons.com/source/include/linux/page-flags.h?v=4.7#L74). 常用的有如下状态
| 页面状态 | 描述 |
|:-------:|:----:|
| PG_locked | page被锁定,说明有使用者正在操作该page |
| PG_error | 状态标志,表示涉及该page的IO操作发生了错误 |
| PG_referenced | 表示page刚刚被访问过 |
| PG_uptodate | 表示page的数据已经与后备存储器是同步的,是最新的 |
| PG_dirty | 与后备存储器中的数据相比,该page的内容已经被修改 |
| PG_lru | 表示该page处于LRU链表上 |
| PG_active | page处于inactive LRU链表, PG_active和PG_referenced一起控制该page的活跃程度,这在内存回收时将会非常有用 |
| PG_slab | 该page属于slab分配器 |
| PG_onwer_priv_1 | |
| PG_arch_1 | |
| PG_reserved | 设置该标志,防止该page被交换到swap |
| PG_private | 如果page中的private成员非空,则需要设置该标志 |
| PG_private_2 | |
| PG_writeback | page中的数据正在被回写到后备存储器 |
| PG_head | |
| PG_swapcache | 表示该page处于swap cache中 |
| PG_mappedtodisk | 表示page中的数据在后备存储器中有对应 |
| PG_reclaim | 表示该page要被回收。当PFRA决定要回收某个page后,需要设置该标志 |
| PG_swapbacked | 该page的后备存储器是swap |
| PG_unevictable | 该page被锁住,不能交换,并会出现在LRU_UNEVICTABLE链表中,它包括的几种pageramdisk或ramfs使用的页, shm_locked、mlock锁定的页 |
| PG_mlocked | 该page在vma中被锁定,一般是通过系统调用mlock()锁定了一段内存 |
| PG_uncached | |
| PG_hwpoison | |
| PG_young | |
| PG_idle | |
内核中提供了一些标准宏,用来检查、操作某些特定的比特位,如:
-> PageXXX(page):检查page是否设置了PG_XXX位
-> SetPageXXX(page):设置page的PG_XXX位
-> ClearPageXXX(page):清除page的PG_XXX位
-> TestSetPageXXX(page):设置page的PG_XXX位,并返回原值
-> TestClearPageXXX(page):清除page的PG_XXX位,并返回原值
2) _count:引用计数,表示内核中引用该page的次数,如果要操作该page,引用计数会+1,操作完成-1。当该值为0时,表示没有引用该page的位置,所以该page可以被解除映射,这往往在内存回收时是有用的。
3) _mapcount:被页表映射的次数,也就是说该page同时被多少个进程共享。初始值为-1,如果只被一个进程的页表映射了,该值为0 。如果该page处于伙伴系统中,该值为PAGE_BUDDY_MAPCOUNT_VALUE-128),内核通过判断该值是否为PAGE_BUDDY_MAPCOUNT_VALUE来确定该page是否属于伙伴系统。
注意区分_count和_mapcount_mapcount表示的是映射次数,而_count表示的是使用次数;被映射了不一定在使用,但要使用必须先映射。
4) mapping:有三种含义
a: 如果mapping = 0,说明该page属于交换缓存(swap cache);当需要使用地址空间时会指定交换分区的地址空间swapper_space。
b: 如果mapping != 0bit[0] = 0,说明该page属于页缓存或文件映射,mapping指向文件的地址空间address_space。
c: 如果mapping != 0bit[0] != 0,说明该page为匿名映射,mapping指向struct anon_vma对象。
通过mapping恢复anon_vma的方法:anon_vma = (struct anon_vma *)(mapping - PAGE_MAPPING_ANON)。
5) index:在映射的虚拟空间(vma_area)内的偏移;一个文件可能只映射一部分,假设映射了1M的空间,index指的是在1M空间内的偏移,而不是在整个文件内的偏移。
6) private:私有数据指针,由应用场景确定其具体的含义:
a:如果设置了PG_private标志,表示buffer_heads
b:如果设置了PG_swapcache标志,private存储了该page在交换分区中对应的位置信息swp_entry_t。
c:如果_mapcount = PAGE_BUDDY_MAPCOUNT_VALUE,说明该page位于伙伴系统,private存储该伙伴的阶。
7) lru:链表头,主要有3个用途:
a:page处于伙伴系统中时,用于链接相同阶的伙伴(只使用伙伴中的第一个page的lru即可达到目的)。
bpage属于slab时,page->lru.next指向page驻留的的缓存的管理结构,page->lru.prec指向保存该page的slab的管理结构。
c:page被用户态使用或被当做页缓存使用时,用于将该page连入zone中相应的lru链表,供内存回收时使用。
因而struct page中使用了大量的联合体union.
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff