内核是如何管理内存的?

本文翻译自 How The Kernel Manages Your Memory

在介绍完进程中虚拟地址空间的布局后,我们来看一看内核是如何管理内存的:

image

内核中使用结构体 task_struct 来描述进程,其中含有一个 mm_struct 类型的成员 mm,该类型是内存管理的主要数据结构,如上图所示,它存储着以下内容:

  • 每一个虚拟地址段的起始地址
  • 进程使用的物理页面 (Physical Page) 的数量
  • 进程使用的虚拟地址空间(Virtual Address Space)的数量
  • 其他额外的信息

还有两个和内存管理相关的重要概念:Virtual Memory Area 和 Page Table

image

每一个 Virtual Memory Area (以下简称 VMA)是一段连续且不重叠的虚拟地址,内核用 vm_area_struct 来描述 VMA,它记录着如下信息:

  • VMA 起始地址
  • 访问权限的标记
  • 映射文件(如果有的话。没有映射文件的 VMA 是匿名映射)

在上图中,每一个 Memory Segment (如 heap,stack 等) 都对应着一个 VMA。

一个进程的所有 VMA 都被保存在它的内存描述符中的一个已排序(根据虚拟地址的起始地址)的链表(在mmap字段中)和红黑树(mm_rb)中。使用红黑树可以使内核快速查找一给定地址所在的 VMA 中。通过查看 /proc/pid_of_process/maps 文件,内核会遍历该进程的 VMA 链表并打印出来。

在 Windows 中,EPROCESS block 大致是 task_struct 和 mm_struct 的混合体。存储 VMA 的结构在 Windows 中叫 VAD(Virtual Address Descriptor),VAD 存放在一棵 AVL 树中。

4GB 的虚拟地址空间被分割成 页(Page),x86 的32位处理器支持 4KB、2MB 和 4MB 大小的页,Linux 和 Windows 都默认使用 4KB 大小的页。一个 VMA 的大小必须是页大小的整数倍。下图是 3GB 大小用户空间的 Page 示例图:

image

处理器使用页表(Page Table)来将虚拟地址转换为物理地址,每一个进程都维护着它自己的页表,当进程切换时,对应的页表(用户空间的)也会发生切换。Linux 在内存描述符使用 pgd 字段指向该进程的页表,每 一个虚拟页面都对应着一个页表项(Page Table Entry,PTE),在x86机器上如下所示:

image

PTE 中有很多 flag,Linux 使用专门的函数来设置和读取这些 flag。

  • P 标志位表示该页面是否已经映射到物理内存,如果该位为0,访问该页将触发缺页中断,Keep in mind that when this bit is zero, the kernel can do whatever it pleases with the remaining fields.
  • R/W 标志位表示该页的读写属性,0为只读;
  • U/S 表示该页的访问权限,0表示只能被内核访问。

以上这些标志位实现了内存的写保护和内核态内存空间。

  • D - Dirty,表示脏页面,如果一个页面被写过,它的 D 位被置为 1;
  • A - Access,如果该页面被访问过(读或写),它的 A 位被置为 1。

这些标志位可以被 CPU 设置,但是只能由内核去 clear 。

最后,PTE 存储着该页面对应的 4K 对齐的起始物理地址。通常页面的最大映射范围是 4GB 的物理内存,但是可以通过 PTE 的其他位进行拓展。

一个虚拟页面是内存保护的基本单位,因为如果从物理页的角度看,同一个物理页可以对应多个虚拟页,从而有不同的保护标志位。注意 PTE 没有关于执行权限的标记,所以在经典 x86 体系机中允许栈上的代码被执行,这容易引致缓冲区溢出攻击。This lack of a PTE no-execute flag illustrates a broader fact: permission flags in a VMA may or may not translate cleanly into hardware protection. The kernel does what it can, but ultimately the architecture limits what is possible.

虚拟页面不实际存储数据,它只是将进程的地址空间映射到底层的物理内存。

在总线上的内存操作比较复杂, 我们可以假设物理地址区间是从0到可用内存按照字节自增的。物理地址空间被内核以页大小为单位划分页帧(Frame)。CPU不知道页帧的存在, 但是页帧是对内核却很重要, 也是内核内存管理最基本的单元。 Linux和Windows都使用4KB大小的页帧(32位模式);下图是一个拥有2G内存的例子:

image

在 Linux 中使用一个描述符和几个标记位来表示页帧,这些描述符整体描述了整个的物理内存,一个页帧的状态总是确定且已知的。物理内存通过伙伴算法进行管理和分配,如果一个页桢当前可通过伙伴算法被分配,则称它是 free 的。

一个已分配的页桢有两种状态:一种是“匿名”的,存放着程序的数据;另一种情况在 page cache 中,存放着 *文件 *或 *块设备 *中的数据(暂不考虑其他奇怪的用法)。Windows 使用 PFN (Page Frame Number) 数据库来管理物理内存。

下面这个图描述了 虚拟内存空间(VMA),页表项和页桢的关系:

image

中间蓝色的方块表示 VMA 中的 page,它的箭头表示通过页表项指向了物理页桢。其中一部分 page 是没有箭头的,这意味着其页表项中的P(resent)标记位被清零,这可能是由于该页不再被使用或其内容已经被 swaped out,这两种情况下访问该页都会导致缺页中断。

VMA 像是内核和上层应用的桥梁,上层应用向内核请求做一件事(内存分配、文件映射等),内核会直接同意,然后修改 VMA,但是内核不会立即去执行上层应用请求的事,真正的执行请求需要一个缺页中断来做。从这个角度看,内核懒惰且不诚实,但这也是 virtual memory 的基准准则。VMA 记录着内核已经“同意”的事,而页表项记录着内核已经“做成”的事。这两个结构是内存管理的主要成员。下面是一个内存分配的流程示例:

内存分配流程

应用程序通过 brk() 系统调用来申请更多内存时后,内核扩展 heap VMA,但新扩展的部分还没有对应上物理内存;当应用程序访问新申请的内存后,系统调用 do_page_fault() 会被调用,它会通过 find_vma() 在 VMA 中查找没有对应上物理内存的 virtual address,如果找到的话,会检查该 VMA 的相关权限并进行下一步操作*,否则会造成 Segmentation Fault.

上面说的“下一步操作”即查找页表项,在上图的情况下,其页表项的 P 标记位会显示其 Not Pressent,(实际上整个页表项是 blank 的,即全为0)。由于当前 VMA 是匿名的,后面会通过 do_anonymous_page() 来进行页桢的分配和更新页表项。

这一步操作也会有另外一种情况,即页表项的 P 标记位为0,但整个页表项不是 blank 的,这意味着它之前被 swaped out,这种情况可以在页表项中找到其 swap 地址,之后由 do_swap_pages() 来处理。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容