内核除了管理本身的内存外,还必须管理用户空间中进程的内存,也就是进程地址空间。
一、地址空间
进程地址空间由进程可寻址的虚拟内存组成,内核允许进程使用这种虚拟内存中的地址。每个进程都有一个32或64位的平坦地址空间,取决于体系结构。平坦是指地址空间范围是一个独立的连续区间。
内存地址要在地址空间范围之内。地址空间中,可被进程访问打的合法地址空间称为内存区域。通过内核,进程可以给自己的地址空间动态增加或减少内存区域。若进程访问了不在有效范围中的内存区域或以不正确的方式访问了有效地址,那么内核就会终止该进程,并返回“段错误”信息。
内存区域可以包含各种内存对象:
- 可执行文件代码的内存映射,称为代码段(text section);
- 可执行文件的已初始化全局变量的内存映射,称为数据段(data section);
- 包含未初始化全局变量,也就是bss段的零页的内存映射
- 用于进程用户空间栈的零页的内存映射
- 每一个诸如C库或动态链接程序等共享库的代码段、数据段和bss也会被载入进程的地址空间;
- 任何内存映射文件
- 任何共享内存段
- 任何匿名的内存映射
二、内存描述符
内核使用内存描述符结构体mm_struct表示进程的地址空间。
该结构体是进程创建时通过allocate_mm宏分配的。若希望父进程和子进程共享地址空间,可以调用clone时设置CLONE_VM标志,这样的进程称为线程。内存描述符通过exit_mm撤销。
三、虚拟内存区域
内存区域在Linux内核中也称作虚拟内存区域(virtual memory Areas,VMAs) 。vm_area_struct结构体描述了指定地址空间内连续区间上的一个独立内存范围。内核将每个内存区作为一个单独的内存对象管理,每个内存区域都拥有一个一致的属性,相应的操作也一致。
四、页表
应用程序访问虚拟地址时,需要将其转化为物理地址,然后处理器才能解析地址访问请求。地址转化需要将虚拟地址分段,使每段虚拟地址都作为一个索引指向页表,而页表项则指向下一级别的页表或指向最终的物理页面。Linux使用三级页表完成地址转化:
- PGD:顶级页表,页全局目录
- PMD:中间页目录
- PTE:页表,指向物理页面
搜索内存中的物理地址速度有限,为了加快搜索速度,多数体系结构实现了翻译后缓冲器(TLB),TLB作为一个将虚拟地址映射到物理地址的硬件缓存,当请求访问一个虚拟地址时,处理器将首先检查TLB中是否有对应缓存,如果命中则立刻返回物理地址;否则就需要通过页表搜索物理地址。