IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> linux内核源码分析之伙伴系统(二) -> 正文阅读

[系统运维]linux内核源码分析之伙伴系统(二)

目录

页分配器API

如何分配页面

分配一个页面情况

分配多个页面情况


页分配器API

? ? ? ? 物理内存管理伙伴系统只能分配2的整数幂个页,即2 的order幂次页

申请页

  • alloc_pages(mask, order)分配2 的order幂次页并返回一个struct page的实例,表示分配的内存块的起始页。alloc_page(mask)是前者在order = 0情况下的简化形式,只分配一页。
  • get_zeroed_page(mask)分配一页并返回一个page实例,页对应的内存填充0(所有其他函数,分配之后页的内容是未定义的)。
  • __get_free_pages(mask, order)和__get_free_page(mask)的工作方式与上述函数相同,但返回分配内存块的虚拟地址,而不是page实例。
  • get_dma_pages(gfp_mask, order)用来获得适用于DMA的页。

释放页

  • free_page(struct page *)和free_pages(struct page *, order)用于将一个或2order页返 回给内存管理子系统。内存区的起始地址由指向该内存区的第一个page实例的指针表示。
  • __free_page(addr)和__free_pages(addr, order)的语义类似于前两个函数,但在表示需要释放的内存区时,使用了虚拟内存地址而不是page实例

如何分配页面

????????实际完成分配任务的是 rmqueue 函数

static inline
struct page *rmqueue(struct zone *preferred_zone,
			struct zone *zone, unsigned int order,
			gfp_t gfp_flags, unsigned int alloc_flags,
			int migratetype)
{
	unsigned long flags;
	struct page *page;

	if (likely(order == 0)) {
		//order=0 是一个页面,从pclist中分配
		page = rmqueue_pcplist(preferred_zone, zone, gfp_flags,
					migratetype, alloc_flags);
		goto out;
	}

	WARN_ON_ONCE((gfp_flags & __GFP_NOFAIL) && (order > 1));
	//加锁关中断
	spin_lock_irqsave(&zone->lock, flags);
	do {
		page = NULL;
		if (alloc_flags & ALLOC_HARDER) {
			//多个内存页面从free_area中分配
			page = __rmqueue_smallest(zone, order, MIGRATE_HIGHATOMIC);
			if (page)
				trace_mm_page_alloc_zone_locked(page, order, migratetype);
		}
		if (!page)
		    //最后调用__rmqueue_smallest函数
			page = __rmqueue(zone, order, migratetype, alloc_flags);
	} while (page && check_new_pages(page, order));
	spin_unlock(&zone->lock);
	if (!page)
		goto failed;
...
	local_irq_restore(flags);

out:
    ...

failed:
	local_irq_restore(flags);
	return NULL;
}

分配一个页面情况

/* Lock and remove page from the per-cpu list */
static struct page *rmqueue_pcplist(struct zone *preferred_zone,
			struct zone *zone, gfp_t gfp_flags,
			int migratetype, unsigned int alloc_flags)
{
	struct per_cpu_pages *pcp;
	struct list_head *list;
	struct page *page;
	unsigned long flags;
    //关中断
	local_irq_save(flags);
	//获取当前CPU下的pcp
	pcp = &this_cpu_ptr(zone->pageset)->pcp;
	//获取pcp下迁移的list链表
	list = &pcp->lists[migratetype];
	//摘取list上的pages结构
	page = __rmqueue_pcplist(zone,  migratetype, alloc_flags, pcp, list);
	if (page) {
		__count_zid_vm_events(PGALLOC, page_zonenum(page), 1);
		zone_statistics(preferred_zone, zone);
	}
	//开中断
	local_irq_restore(flags);
	return page;
}

static struct page *__rmqueue_pcplist(struct zone *zone, int migratetype,
			unsigned int alloc_flags,
			struct per_cpu_pages *pcp,
			struct list_head *list)
{
	struct page *page;
	do {
		if (list_empty(list)) {
			//如果list为空,就从这个内存区域中分配一部分页面到pcp中来
			pcp->count += rmqueue_bulk(zone, 0,
					pcp->batch, list,
					migratetype, alloc_flags);
			if (unlikely(list_empty(list)))
				return NULL;
		}
		//获取list上第一个page结构
		page = list_first_entry(list, struct page, lru);
		list_del(&page->lru);
		//减少pcp页面计数
		pcp->count--;
	} while (check_new_pcp(page));
	return page;
}

分配多个页面情况

调用 __rmqueue_smallest 函数,从 free_area 数组中分配。

static __always_inline
struct page *__rmqueue_smallest(struct zone *zone, unsigned int order,
						int migratetype)
{
	unsigned int current_order;
	struct free_area *area;
	struct page *page;

	/* Find a page of the appropriate size in the preferred list */
	for (current_order = order; current_order < MAX_ORDER; ++current_order) {
		//获取current_order对应的free_area
		area = &(zone->free_area[current_order]);
		//获取free_area中对应migratetype为下标的free_list中的page
		page = get_page_from_free_area(area, migratetype);
		if (!page)
			continue;
		//拖链page
		del_page_from_free_area(page, area);
		//分割伙伴
		expand(zone, page, order, current_order, area, migratetype);
		set_pcppage_migratetype(page, migratetype);
		return page;
	}

	return NULL;
}

????????首先要取得 current_order 对应的 free_area 区中 page,若没有,就继续增加current_order,直到最大的 MAX_ORDER。要是得到一组连续 page 的首地址,就对其脱链,然后调用 expand 函数分割伙伴。

static inline void expand(struct zone *zone, struct page *page,
	int low, int high, struct free_area *area,
	int migratetype)
{
	unsigned long size = 1 << high;

	while (high > low) {
		area--;
		high--;
		size >>= 1;//每次循环左移一位

		VM_BUG_ON_PAGE(bad_range(zone, &page[size]), &page[size]);

		//标记为保护页,当其伙伴被释放时,允许合并
		if (set_page_guard(zone, &page[size], high, migratetype))
			continue;
		//把另一半pages加入对应的free_area中
		add_to_free_area(&page[size], area, migratetype);
		//设置伙伴
		set_page_order(&page[size], high);
	}
}

参考

《深入linux内核架构》

剖析Linux内核物理内存管理-大学生教程-腾讯课堂


  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-05-05 12:01:58  更:2022-05-05 12:06:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 18:37:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码