Linux内核内存管理之页框管理

背景:

页框管理是Linux系统的基本功能,主要负责维护RAM资源,完成系统对RAM资源请求的分配。 Linux 把 RAM 每

划分为一个页框,这样正好与页大小相等或为页框的整数倍,便于请求页框。

利用页框机制有助于灵活分配内存地址,因为分配时不必要求必须有大块的连续内存,系统可以离散寻找空闲页凑出所需要的内存供进程使用。 虽然如此,但是实际上系统使用内存时还是倾向于分配连续的内存块,因为分配连续内存时,页表不需要更改,因此能降低TLB的刷新率。

为了能够在保存连续页的同时,又能满足对启动内存需求的分配, 页框在分配上使用了伙伴系统

下面我们通过细节介绍下过程.

页框管理的数据结构:

page

在内核中每一个物理页框对应有一个页描述符(struct page) , 所有的页描述符存放在一个mem_map 线性数组之中。 每一个页框的页框号(address >> PAGE_SHIFT), 为页描述符在 mem_map 数组的位置(下标)。 每一个描述符占用32字节长度的大小,所以 mem_map 所用空间约占RAM %1(32B/32KB)左右.

关于页描述符,位于include/linux/mm.h 可以找到其定义:
struct page {
  /**
	 * 一组标志,
	 *   - 也对页框所在的管理区进行编号
	 *   - 描述页框的状态: 如 页被锁定,IO错误, 被访问,被修改位于活动页,位于slab 等。
	 */
	page_flags_t flags;

  /**
	 * 页框的引用计数。
	 *   - 小于0表示没有人使用。
	 *   - 大于0表示使用人数目
	 */
	atomic_t _count;

  /**
	 * 页框中的页表项数目(没有则为-1)
	 *		-1:		表示没有页表项引用该页框。
	 *		0:		表明页是非共享的。
	 *		>0:		表示而是共享的。
	 */
	atomic_t _mapcount;

  /**
	 * 可用于正在使用页的内核成分(如在缓冲页的情况下,它是一个缓冲器头指针。)
	 * 如果页是空闲的,则该字段由伙伴系统使用。
	 * 当用于伙伴系统时,如果该页是一个2^k的空闲页块的第一个页,那么它的值就是k.
	 * 这样,伙伴系统可以查找相邻的伙伴,以确定是否可以将空闲块合并成2^(k+1)大小的空闲块。
	 */
	unsigned long private;


  /**
	 * 当页被插入页高速缓存时使用或者当页属于匿名页时使用)。
	 * 		如果mapping字段为空,则该页属于交换高速缓存(swap cache)。
	 *		如果mapping字段不为空,且最低位为1,表示该页为匿名页。同时该字段中存放的是指向anon_vma描述符的指针。
	 *		如果mapping字段不为空,且最低位为0,表示该页为映射页。同时该字段指向对应文件的address_space对象。
	 */
	struct address_space *mapping;

  /**
	 * 作为不同的含义被几种内核成分使用。
	 *  - 在页磁盘映象或匿名区中表示存放在页框中的数据的位置。不是页内偏移量,而是该页面相对于文件起始位置,以页面为大小的偏移量
	 *  - 或者它存放在一个换出页标志符。表示所有者的地址空间中以页大小为单位的偏移量,也就是磁盘映像中页中数据的位置 page->index是区域内的页索引或是页的线性地址除以PAGE_SIZE
	 */
	pgoff_t index;	

  struct list_head lru;	// 包含页的最近最少使用的双向链表的指针, 用于伙伴系统

#if defined(WANT_PAGE_VIRTUAL)
  /**
	 * 如果进行了内存映射,就是内核虚拟地址。对存在高端内存的系统来说有意义。
	 * 否则是NULL
	 */
	void *virtual;
#endif /* WANT_PAGE_VIRTUAL */

与页描述符相关的宏定义

内核可以使用相关方法来定位每一个地址对应的页描述符地址。

#define __pa(x)			((unsigned long)(x)-PAGE_OFFSET)
#define pfn_to_page(pfn)	(mem_map + (pfn))                          // 根据页框号来定位页描述符
#define virt_to_page(kaddr)	pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)   // 内核线性地址定位页描述符
#define page_address(page) ((page)->virtual)                         // 返回描述符对应的内核虚拟地址(内存映射)

节点 node

在 linux 2.6 版本内核中支持一种 NUMA(非一致内存访问) 模型, 它支持根据CPU对不同内存单元访问时间的不同,将物理内存划分为不同的节点,每个节点中,指定CPU对节点内任意内存单元访问时间都是相同的。

每个节点具有一个节点描述符(struct pg_data_t) 的数据结构来表示, 所有的节点都使用单链表来统一维护(pg_data_t-> pgdat_next)。

节点描述符的定义位于include/linux/mmzone.h中 :
typedef struct pglist_data {
	struct zone node_zones[MAX_NR_ZONES];              // 节点管理区描述符数组
	struct zonelist node_zonelists[GFP_ZONETYPES];     // 页分配器使用的zonelist数据结构的数组。
	int nr_zones;                                      // 节点中管理区的个数
	struct page *node_mem_map;                         // 节点中页描述符的数组
	struct bootmem_data *bdata;                        // 用在内核初始化阶段
	unsigned long node_start_pfn;                      // 节点中第一个页框的下标
	unsigned long node_present_pages;                  // 内存结点的大小,不包含空洞(以页为单位)
	unsigned long node_spanned_pages;                  // 节点的大小,包括空洞
	int node_id;                                       // 节点标识符
	struct pglist_data *pgdat_next;                    // 内存节点链表的下一项。该字段构成node单链表。
	wait_queue_head_t kswapd_wait;                     // Kswapd页换出守护进程使用的等待队列
	struct task_struct *kswapd;                        // 指针指向kswapd内核线程的进程描述符。
	int kswapd_max_order;                              // Kswapd将要创建的空闲块大小取对数的值。
} pg_data_t;

但在8086体系架构中只使用了UMA(一致访问内存) 模型,从理论上并不需要NUMA的支持, 但是为了代码可移植性,还是把所有的RAM所有物理内存都放在一个节点描述符中。

/**
 * 支持NUMA。
 * 这个元素由contig_page_data表示。它包含在一个只有一个结点的链表中,这个链表被pgdat_list指向。
 */
struct pglist_data contig_page_data = { .bdata = &contig_bootmem_data };

管理区描述符 zone

理想架构中,页框作为内存存储单元可以存储内核和用户数据,磁盘数据缓存等等。任何页数据都可以存在任何页框中,没有任何限制。然而真实的架构有着硬件限制。

Linux内核必须处理 80x86 架构的两个硬件限制:

旧ISA总线的DMA处理器有一个严格的限制:它们只能寻址RAM的前16MB空间。

现代32位计算机有很多RAM,CPU不能直接访问所有的物理内存,因为线性地址空间太小了。(内核线性地址只有3G到4G这1GB,而RAM当前比这大得多,设计成一对一肯定是不行的,所以映射机制是个复杂的大杂烩。)

为了应付这两个限制,Linux在80x86 UMA架构中分割物理内存node成3个zones:

ZONE_DMA : 包含低16MB的内存页框,用于兼容旧ISA总线DMA处理器

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值