内存管理浅谈

REF

https://www.ibm.com/developerworks/cn/linux/l-cn-pagerecycle/index.html

http://www.cnblogs.com/visayafan/archive/2011/12/24/2300758.html

内存管理

内存管理

本文简单介绍 Linux Kernel 怎么管理内存. 包含内核物理内存管理和用户空间内存管理两大部分.

1. Page Frame

Linux Kernel 以物理 Page (即 Page Frame) 为基本单位来进行物理内存管理. 而对不同的 Architecture, Page Frame 的大小也不一样, 不过一般的 32 位的 Architecture 的 Page Frame 大小为 4K Bytes. Kernel 用一个数据结构 struct page 来表示系统中的一个 Page Frame (下面只列出了 4 个重要的 fields):

struct page {
    unsigned long            flags;
    atomic_t                 _count;
    struct address_space     *mapping;
    struct list_head         lru;
};

4个重点字段:

flags: 标识物理页面的状态 (例如, 是否是脏页)

_count: 物理页面的引用计数, 一旦为 0, 说明这个页面没有被使用, 是 free 的

lru: 链表头. 多个Page Frames可以用链表组织起来, 例如, active 链表 和 inactive 链表 (以后介绍页面回收的时候会详细介绍)

mapping 分两种情况:

如果物理页面被 Page Cache 使用, 并映射到一个文件, 那么 mapping 指向映射的文件的 inode 的 address_space 对象 (以后介绍 Page Cache 时会有更详细的介绍)

如果物理页面是一个匿名页, 那么 mapping 指向 anon_vma 对象, 而不是 address_space 对象 (关于匿名页, 以后会有详细介绍)

2. 进程地址空间

从每个用户空态进程的角度来看, 它能使用的内存是一个独享的平坦线性的 32位 或 64位的虚拟地址空间 (一个大数组), 即进程地址空间 (Process Address Space).

而这个大的内存空间 (32位的话, 有 4G) 又可以被分成很多内存区域 (Memory Areas), 每个 Memory Area 是一个连续的地址空间, 而且各个 Memory Area 不会重叠.

对进程地址空间 (Process Address Space) 中的不同的 Memory Areas, 用户进程有不同的操作权限 (可读, 可写, 可执行). 例如, 一块区域是代码段, 那么, 用户进程对这块区域只有可读和可执行的权限.

Kernel 用一个数据结构来描述进程地址空间: struct mm_struct, 其中包含了进程地址空间的所有信息 (每个进程对应一个 task 结构, 其中有一个 mm field 指向这个进程对应的 mm_struct).

下面特别介绍 mm_struct 的 4 个关键 fields:

  • mmap: 本地址空间包含的所有的 Memory Areas 的集合. mmap 是一个链表, 当遍历所有 Memory Areas 时使用;
  • mm_rb: 和 mmap 字段一样, mm_rb 也是本地址空间所有 Memory Areas 的集合,不过不是链表, 而是一个 红黑树, 用来快速查找某个 Memory Area;
  • pgd: 指向本进程Page Table 的指针 (Page Table 后面会详细讲)
  • mm_users: 有几个进程正在使用这个地址空间 (例如: 如果两个线程共享这个地址空间, 那么 mm_users 为 2)

3. VMA

在 Linux Kernel 中, 组成进程地址空间的 Memory Areas 被简称为 VMAs. VMA 的种类:

和文件相关的 VMA: 代码/库, 数据文件, 共享内存, 设备; 这些 VMA 的内容都是来至于文件的.
匿名 VMA: Stack, Heap, CoW pages; 这些 VMA 的内容都是用户程序管理的.
Linux Kernel 用 struct vm_area_struct 来描述 Memory Area. 每个 Memory Area 对应一个 vm_area_struct, 下面介绍 vm_area_struct 的关键 fields:

vm_mm: 指向 VMA 对应的 mm_struct;

vm_start: vma 的起始位置 (低位)

vm_end: vma 的结束位置 (高位). vm_end - vm_start就是这个 VMA 的 size.

一组函数指针; 这些函数实现了在这个 VMA 上的各种操作 (page fault, open, close ...)

下图展示了管理一个进程地址空间 (Process Address Space) 怎么管理和它相关的 VMAs.

970272-20170913115135844-196406112.png

4. Page Table

用户进程地址空间只是一个虚拟地址空间, 当进程在运行时, 操作系统必须把进程正在使用的虚拟地址和物理地址对应起来.

Linux 按页管理内存, 当用户进程要存取某个 Page, 但这个 Page 还没有存在在物理内存中, Linux 触发一次 Page Fault, 把这个 Page 和 物理 Page Frame 对应起来.

Linux Kernel 利用 Page Table 来做虚拟地址到物理内存的地址映射 (每个进程都有自己的 Page Table).

Linux Kernel 使用 4 级页表: PGD->PUD -> PMD->PTE; 操作系统只要先做好设置, 一般来说, 硬件 (CPU) 会自动做虚拟地址到物理地址的映射 (MMU);

而且会利用 TLB 来加速映射 (映射缓存到 TLB).

一旦切换进程, 也要切换 Page Table, 同时可能也要刷新 TLB. 也就是说, 各个进程的页表是隔离的, 不会互相影响.

下图展示了利用页表把虚拟页映射到一个 Page Frame.

970272-20170913115148375-2109097691.png

5. 反向 Mapping

上一节讲了了虚拟内存 Page 到 物理内存 Page Frame 的映射.

而很多时候, 某个 Page Frame 可能会被多个 Page 映射 (例如一个共享库被多个进程共享); 这种物理页被多个虚拟页共享的机制可以节省内存.

但是同时也引入了新的问题:

如果这个物理页长时间没有被使用, 当系统内存紧张时, 可能会把这个物理页换出到外存. 此时也需要通知那些映射到这个物理页的进程修改页表.
那么怎么才能知道某个 Page Frame 被哪些 Pages 映射了呢?
这就需要引入一个 反向映射 (反向 Mapping) 机制. Kernel 会利用一个数据结构来记录这个物理页被哪些 VMAs 使用.
反向 Mapping 这里只是先提一下. 以后介绍 Page Cache 时会介绍更多的细节.

地址空间

970272-20170913133922813-1131575422.png

  1. 每个进程有个进程描述符task_struct,其中有mm域指向该进程的内存描述符mm_struct。

  2. 每个进程都拥有一个内存描述符,其中有PGD域,指向该进程地址空间的全局页目录;mmap域指向第一个内存区域描述符vm_area_strut1。

  3. 进程通过内存区域描述符vm_area_struct管理内存区域,每个内存区域描述符都有vm_start和vm_end域指向该内存区域的在虚拟内存中的起始位置;vm_mm域指向该进程的内存描述符;每个vm_area_struct都有一个anon_vma域指向该进程的anon_vma;

  4. 每个进程都有一个anon_vma,是用于链接所有vm_area_struct的头结点,通过vm_area_struct的anon_vma_node构成双循环链表。

最终形成了上图。

转载于:https://www.cnblogs.com/muahao/p/7514221.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值