Linux系统的物理内存管理

1、“物理内存”和“虚拟内存”

一般我们说内存,即指“物理内存”。那为什么本文要强调“物理内存”的概念呢?这是为了和“虚拟内存”的概念区分。常见的操作系统,例如Linux和windows,都是建立在存储映射机制的基础上。不同计算平台的“物理内存”大小不尽相同,其在操作系统初始化时,根据硬件情况确定,而“虚拟内存”的大小是由操作系统本身决定,例如32位系统的每一个用户态进程拥有的“虚拟内存”大小就是固定4G。

简单地说,同一个操作系统不管运行在什么机器上,每个进程拥有的虚拟内存空间都是固定的;而同一台机器不管在上面运行什么操作系统,其物理内存大小也是固定的。

用户应用程序始终使用虚拟地址,无需关心物理地址。虚拟地址到物理地址的映射则由操作系统来管理。操作系统正是借助这样的机制,隐藏了不同机器的内存差异,使得同一个应用程序可以运行在各种不同大小的物理内存的机器上。

2、Linux“物理内存”管理数据结构

Linux采用页式存储管理,顾名思义就是将物理内存分成一页一页的,内存分配和回收的最小单位即为一页。实际上Linux系统每一页的大小为4K,例如一台插有1G内存条的机器,则将“物理内存”分为256k个物理页面。

2.1 Linux系统如何感知和管理物理页面

操作系统启动前,运行BIOS程序,这段程序初始化硬件,操作系统正是通过BIOS程序获取了物理内存大小。系统加载后,初始化过程创建了一个全局数据数组mem_map,每个数据元素为struct page。每一个struct page表示一个物理页面,page结构大小32 bytes,mem_map的数组大小即为物理页面的个数。

struct page虽然是按mem_map数组的顺序来存储,但是分配和回收物理页面显然不会采用数组顺序遍历的方式,因为数组的组织方式将所有页面都混合在一起不易管理,并且查找效率比较低。实际上在Linux系统中,mem_map中的每个page又被按所属管理区(DMA区域、普通区、高区)、是否被空闲、最近使用频率等因素分别重新组织到不同链表中。

那么Linux管理一个物理页面都需要哪些信息呢?一般容易想到,物理页面的大小,物理页面的物理地址范围。实际上述信息都不需要,原因是物理页面的大小固定4K bytes,并且每个页面的物理地址范围也可以根据页面大小和mem_map数组下标推算。Linux的page结构如下,可以看出,page结构本身并不复杂,其中主要包含三类字段。一类是链表指针,例如list、next_hash、pprev_hash、lru等;还有一类是物理页面本身的信息,例如index、count、flags、age、zone、virtual;最后一类是为物理页面映射到磁盘设置,例如mapping、buffer等。

struct page {
    struct list_head list;    // page链表
    struct address_space *mapping;    // 所属文件映射
    unsigned long index;    // mem_map数组下标
    struct page *next_hash;    // page组织成hash表
    acomic_t count;    // 页面使用计数
    unsigned long flags;    // 页是否脏、是否锁定等标志
    struct list_head lru;    // 最近最久未使用链表
    unsigned long age;
    wait_queue_head_t wait;    // 等待队列链表
    struct page **pprev_hash;
    struct buffer_head * buffers;    // 物理内存到块设备偏移
    void *virtual;    // 页的虚拟地址
    struct zone_struct *zone;    // 所属管理区
}

2.2 Linux的物理内存分区

Linux系统将物理内存分成了几个管理区(DMA区域、NORMAL区、HIGHMEM区)。不同管理区的物理内存用途各不相同。

DMA区域就是给DMA控制器使用,不参与分配和回收。

NORMAL用作操作系统内核和用户进程正常映射的区域。这个区域线性映射到内核的虚拟地址空间,可以被内核直接访问。

需要注意的是,32位Linux系统当内存大小大于1G时,还可能存在HIGHMEM区内存。HIGHMEM区的存在是因为32位系统的内核虚拟地址空间只有1G,如果不设计这个区域,那么内核将无法访问高于1G的物理内存,这决定了HIGHMEM区不能永久映射到内核空间。另外,64位系统由于内核地址空间很多,就不存在HIGHMEM区。

Linux将内存的管理区用zone_struct表示,如下所示,可以将zone_struct理解为物理内存管理的顶层数据结构。由于系统中只存在2或3个管理区,因此系统全局只有2到3个zone_struct结构。

struct free_area {
    struct list_head free_list;    // 空闲区域链表
    unsigned int *map;    // 
};
struct zone {
    spinlock_t lock;
    unsigned long free_pages;
    unsigned long inactive_clean_pages;
    unsigned long inactive_dirty_pages;
    ...
    free_area_t free_area[MAX_ORDER];    // 空闲物理页面,分别表示大小为1,2,4,...,2MAX_ORDER个物理页面的连续内存
    ...
};

2.3 Linux的物理内存分配策略

由于Linux物理内存分配的最小单位是页,这其中必然涉及到怎么分配物理内存,才能使得物理内存的利用率更高,内存碎片越少的问题。

伙伴系统
从struct zone中空闲内存结构free_area也可以看出,Linux将空闲内存按大小分别组织链接成不同链表。

物理内存分配
假如分配3个页面大小的物理内存,则优先选择从大小为1的空闲区域分别选择3块。如果大小为1的空闲区域没有3个,则从大小为2选择。总之就是优先选择小内存的空闲区域拼凑。当然前提是申请的内存没有连续的要求,如果要求连续,则先从大小为3的链表选取,如果没有再考虑将大内存拆到小内存。

物理内存回收
物理内存回收时,会查找回收区域上部和下部是否空间,如果空闲则将小内存与连续的空闲区域合并成大块空闲区域。

你可能感兴趣的:(操作系统)