LINUX内存管理子系统是采用请求调页式的虚拟存储器技术实现的。在32位硬件平台上,LINUX逻辑地址为32位,因此每个进程的虚拟地址空间为4GB,操作系统占用了高端的1GB,低端的3GB则留给用户程序使用。
对于每个进程,LINUX使用mm_struct结构体来描述进程使用的地址空间的各种信息。在每个进程PCB(task_struct)中包含一个mm_struct的指针指向本进程的mm_struct。每个进程与用户空间相关的各种信息都存放在mm_struct结构体中,其中包含本进程的页目录表的地址,本进程的用户区组成情况等重要信息。
每个进程的用户区由一组vm_area_struct结构体组成的链表来描述。每个段如代码段、数据段、栈段等。
分页模型:
LINUX采用4级页表结构。第一级页表的起始地址保存在PCB中,页表中的每个表项保存了下级页表的页面号(即起始地址)。
为了实现跨平台运行,LINUX提供了一系列转换宏使得内核可以访问特定进程的页表。
内存映射
可执行文件内容仅仅映射到了对应进程的虚拟地址空间中,并没有调入物理内存。当程序开始运行并使用到这部分时,linux才通过缺页中断把他们从磁盘上调入内存。这种将文件连接到进程虚拟地址空间的过程称为内存映射。
物理内存模型:
适应NUMA硬件结构,LINUX建立物理内存模型,对于一个CPU来说,使用性能相同的一段内存空间被成为一个节点。
物理页面的分配:
LINUX使用free_area数组来管理空闲页面,每一个区域都有一套这样的管理结构。每个元素含有两个重要指针:一个指向一个位图区域;而另一个指向由page结构体(页面信息)作为元素的双向链表。
BUDDY(伙伴)算法:管理页面的分配和回收。以2的幂次方作为单位来管理内存。
页面回收:解决的关键问题就是如何快速地确定回收的页面能否与已经存在的相邻空闲页面组成更大的空闲页块。free_area数组中的每个元素指向的位图正是用于这一目的。当页面块被释放时,代码将通过位图检查是否有相同大小的相邻或者buddy内存块存在。如果有,则将它们结合起来形成一个大小为原来两倍的新空闲块。
SLab缓存管理:buddy只适合对大块内存的请求。对于需要小内存区的请求,如果也按照页面为单位分配,将是极大浪费内存资源。
slab分配器算法:
设计目标:
对象服用、硬件cache利用率。
页面换出或丢弃
为了保证进程需要页面时能够及时申请到足够的物理内存,LINUX利用内核线程kswapd来定期检查系统内的空闲物理内存总量。
kswapd通过以下三个途径来减少系统中使用的物理页面的个数:
减少缓存与页面cache的大小;
将系统V类型的共享内存页面交换出去;
换出或者丢弃属于进程用户空间的页面。
(转载)脏页-linux内核中的概念,因为硬盘的读写速度远赶不上内存的速度,系统就把读写比较频繁的数据事先放到内存中,以提高读写速度,这就叫高速缓存,linux是以页作为高速缓存的单位,当进程修改了高速缓存里的数据时,该页就被内核标记为脏页,内核将会在合适的时间把脏页的数据写到磁盘中去,以保持高速缓存中的数据和磁盘中的数据是一致的。
交换cache
当页面交换到交换空间时,LINUX总是避免页面写,除非必须这样做。只要页面在内存中没有被写过,则交换空间中的复制是有效的。LINUX使用交换缓存来跟踪这些页面,为一个页表入口链表,每个对应于系统中的物理页面。当LINUX需要将一个物理页面交换到交换空间时,它将检查交换缓存,如果对应此页面存在有效入口,则不必将这个页面写到交换空间中。因为自从上次从交换空间中将其读出来,内存中的这个页面还没有被修改。