Linux 伙伴系统

news2024/9/23 15:19:57

Linux 伙伴系统

  • 前言
  • 一、rmqueue
    • 1.1 流程图
    • 1.2 函数原型
    • 1.3 通过PCP分配
    • 1.4 大阶页面分配
  • 二、__rmqueue
    • 2.1 流程图
  • 三、__rmqueue_pcplist
    • 3.1 流程图
  • 四、__rmqueue_fallback
  • 五、__rmqueue_smallest
    • 5.1 源码
      • 5.1.1 get_page_from_free_area
      • 5.1.2 del_page_from_free_list
      • 5.1.3 expend

前言

本文从伙伴系统的页面分配函数 rmqueue() 开始,此函数作用是,从给定的zone中分配2^order的页面,在5.14.2中,rmqueue() 仅仅被 get_page_from_freelist() 调用,get_page_from_freelist() 更是重量级,由页面分配的核心函数 __alloc_pages() 所调用的。以上及下面所有讲到的函数的定义,均在mm/page_alloc.c 中定义。

而暂时仅仅着眼于伙伴系统的核心分配函数:rmqueue()

一、rmqueue

1.1 流程图

在这里插入图片描述

1.2 函数原型

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)

preferred_zone,是根据gfp_flags算出来的,是在核心函数__alloc_pages中确定的,表示本次页面分配的请求,最先考虑从那个zone中进行分配,但是这个参数在本函数中不起关键作用,因为函数rmqueue的作用是从指定的zone中分配page,而这个指定的zone是第二个参数,前面讲过,get_page_from_freelist会调用rmqueue,而get_page_from_freelist作用就是遍历所有可用的zone,而preferred_zone是遍历过程的第一个被选中的zone.

zone, rmqueue正是从改zone的free_list中,选择page进行分配

order,指分配多少阶的page,与伙伴系统息息相关

gfp_flags, alloc_flags 都是分配配置,讲到再说

migratetype,迁移类型

1.3 通过PCP分配

/// 代码中省略了CMA相关的部分 
 if (likely(pcp_allowed_order(order))) {
    page = rmqueue_pcplist(preferred_zone, zone, order, gfp_flags, migratetype,
                           alloc_flags);
    goto out;
  }

首先通过函数pcp_allowed_order检查是否需要通过PCP进行分配,所谓PCP即Per_CPU方式,即内核将变量缓存给每一个CPU上,不同的CPU都保留有自己的副本,这样不同的CPU可以并发的访问自己的这部分变量而无需上锁。

在之前的内核版本上,只有order==0,即分配1个页面是才会采用PCP方式进行分配,现在时代变了,当order<=3时都会使用PCP方式,pcp_allowed_order代码如下:

// 注意,以下代码我删除了关于透明大页的情况
static inline bool pcp_allowed_order(unsigned int order)
{
	if (order <= PAGE_ALLOC_COSTLY_ORDER) // PAGE_ALLOC_COSTLY_ORDER == 3
		return true;
	return false;
}

如果 order<=3 , 那么将调用rmqueue_pcplis,从pcplist中分配page,此函数我们将在下一小节展开.

1.4 大阶页面分配

/// 代码中省略了CMA相关的部分
if (alloc_flags & ALLOC_HARDER) {
  page = __rmqueue_smallest(zone, order, MIGRATE_HIGHATOMIC);
  if (page)
    trace_mm_page_alloc_zone_locked(page, order, migratetype);
}
if (!page)
  page = __rmqueue(zone, order, migratetype, alloc_flags);

ALLOC_HARDER,含义是尽力分配,因为为了保障其不会失败,将优先从MIGRATE_HIGHATOMIC中进行分配,MIGRATE_HIGHATOMIC是一种迁移类型,但是和迁移关系不大,你可以理解为专门为ALLOC_HARDER标志预留的一部分空间,如果失败,则调用__rmqueue函数走传统的路径进行分配。

问题在于,MIGRATE_HIGHATOMIC的page是怎么来的呢?因为在伙伴系统初始化的时候,会将所有的page定义为MIGRATE_MOVABLE,这是通过函数memmap_init_range实现的:

/**
 ...
 * All aligned pageblocks are initialized to the specified migratetype
 * (usually MIGRATE_MOVABLE). Besides setting the migratetype, no related
 * zone stats (e.g., nr_isolate_pageblock) are touched.
 */
void __meminit memmap_init_range(unsigned long size, int nid, unsigned long zone,
		unsigned long start_pfn, unsigned long zone_end_pfn,
		enum meminit_context context,
		struct vmem_altmap *altmap, int migratetype)
{

然后MIGRATE_UNMOVABLE,MIGRATE_RECLAIMABLE类型可以通过后备列表的方式从其他由page的list上“偷”page:

static int fallbacks[MIGRATE_TYPES][3] = {
	[MIGRATE_UNMOVABLE]   = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE,   MIGRATE_TYPES },
	[MIGRATE_MOVABLE]     = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_TYPES },
	[MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE,   MIGRATE_MOVABLE,   MIGRATE_TYPES },
};

但是显然不包括MIGRATE_HIGHATOMIC,其实此类型的page是通过函数reserve_highatomic_pageblock进行分配的,系统最多会给每个zone分配(zone_managed_pages(zone) / 100) + pageblock_nr_pages)个page,即zone管理的内存页的百分之一,并且保底是pageblock_nr_pages个,pageblock可以理解为一个4MB的页块,即对应伙伴系统的最大可分配页,page的迁移类型是按页块为单位进行修改的,修改页块迁移类型的函数是set_pageblock_migratetype。
那么reserve_highatomic_pageblock在什么时候被调用呢?是在get_page_from_freelist()函数之后,我在前言中讲过了。

因此逻辑是这样的,一旦有分配请求包含了ALLOC_HARDER,那么将会尝试从MIGRATE_HIGHATOMIC中申请,但是第一次肯定会失败,,于是就会从正常的流程中申请,成功后就会将所得page所在的pageblock整个加入到MIGRATE_HIGHATOMIC类型的列表中,但是并不能每次都能成功加进入,上线就是(zone_managed_pages(zone) / 100) + pageblock_nr_pages)个。

二、__rmqueue

从下面的流程图,可以看到,__rmqueue其实是在循环的执行__rmqueue_smallest,__rmqueue_smallest是真正从伙伴系统中那页面的过程,即对于伙伴系统分割大order,填充给小order过程的实现,也就是伙伴系统的最核心算法。

但是__rmqueue_smallest,只能从指定的迁移类型列表中拿页面,当该迁移类型页面不足时,就需要想其他的迁移类型借,我前面给出的那个二维数组:

static int fallbacks[MIGRATE_TYPES][3] = {
	[MIGRATE_UNMOVABLE]   = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE,   MIGRATE_TYPES },
	[MIGRATE_MOVABLE]     = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_TYPES },
	[MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE,   MIGRATE_MOVABLE,   MIGRATE_TYPES },
};

就表示了,借页面的顺序,如MIGRATE_UNMOVABLE不足时,首先从MIGRATE_RECLAIMABLE中借,然后从MIGRATE_MOVABLE中借。

而__rmqueue_fallback的作用就是,如果能成功接到,就把页面从被借的list放到指定的迁移类型列表中,此时将返回true,然后再执行__rmqueue_smallest,去分割order,分配页面。如果借不到,那么只能返回NULL

2.1 流程图

在这里插入图片描述

三、__rmqueue_pcplist

rmqueue_pcplist主要作用是根据order的大小,选择对应的pcp_list,并在调用__rmqueue_pcplist获取page之前上锁,因此我们直接看__rmqueue_pcplist的实现.

3.1 流程图

在这里插入图片描述
其实逻辑很简单,如果list为空,那么就调用rmqueue_bulk申请一批次的page填充到pcp_list中,主要就是这个逻辑。

而rmqueue_bulk同样也是调用__rmqueue实现的,那我我们现在来分析一下,__rmqueue的两个主要函数__rmqueue_fallback和__rmqueue_smallest

四、__rmqueue_fallback

此函数的作用是,当指定的迁移类型的列中中page不足时,从该迁移类型的后备列表中偷取空闲的page补充到指定的迁移类型列表中。并且系统初始化时,所有的page都是MIGRATE_MOVABLE类型.

而后被列表就是我们两次前面提到的:

/// 需要注意,没考虑CMA的情况
static int fallbacks[MIGRATE_TYPES][3] = {
	[MIGRATE_UNMOVABLE]   = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE,   MIGRATE_TYPES },
	[MIGRATE_MOVABLE]     = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_TYPES },
	[MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE,   MIGRATE_MOVABLE,   MIGRATE_TYPES },
};

可以看到,MIGRATE_UNMOVABLE、MIGRATE_MOVABLE和MIGRATE_RECLAIMABLE是互为后备列表的.

这个函数很长,但是实际上东西不好说,其大原则就是,在偷取page的时候要尽可能把整个pageblock都偷过去,实在买办法也要尽可能偷大的order。

然后要修改整块pageblock的类型,如果不是整块偷的,那么就会出现混合的情况,即同一pageblock的page的位于不同的迁移列表。但是整块的pageblock的类型是统一的,基本上是谁多是谁的类型,比如偷取的部分order>一半,那么就要全部改成对应了的类型。

五、__rmqueue_smallest

5.1 源码

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) {
		area = &(zone->free_area[current_order]);
		page = get_page_from_free_area(area, migratetype);
		if (!page)
			continue;
		del_page_from_free_list(page, zone, current_order);
		expand(zone, page, order, current_order, migratetype);
		set_pcppage_migratetype(page, migratetype);
		return page;
	}
	return NULL;
}

函数非常的短小,整个循环恰好符合了伙伴系统的核心思想,即一层层的的往下拆分,直到合适的order的page.

5.1.1 get_page_from_free_area

即直接从指定迁移类型的list中找到page,如果失败,则寻找的order++;

5.1.2 del_page_from_free_list

负责将其list中拿走,操作包括list_del,list的计数–等;

5.1.3 expend

expend 是负责拆分的函数,即如果从odrer较大的list中选择了一一组page进行拆分,那么拆分后剩余的page将被添加到较低order的list中,注意,我们只需要将每一组的page的首个page添加到free list中即可,如我要获取order0的page,但是是从order3的块中获取的:
在这里插入图片描述
page指向的是order=3的块,page保存在伙伴系统order=3的free_list上,当我们需要以order=0返回page时,就需要把整个块拆分, 并以此将:

  • page + 2 ^0 的page添加到 order-0 free_list
  • page + 2 ^1 的page添加到 order-1 free_list
  • page + 2 ^2 的page添加到 order-2 free_list
    extend函数,就是做这件事

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/79290.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从零开始把 SpringBoot 搬到 K8s 上运行,我用了这几步!

前言 大家好&#xff0c;我是网管。咱们的 K8s 入门和实践&#xff0c;在经历了三篇理论知识的后&#xff0c;相信各位都已经期待许久&#xff08;可能的吧&#xff09;&#xff0c;就差私信我&#xff0c;你整着理论整半天有啥用&#xff0c;本大人写的程序怎么能放到 K8s 上运…

Istio初探

Istio初探 前置环境&#xff1a;docker 一、安装k8s&#xff1a; ● https://segmentfault.com/a/1190000042204035 1、 https://github.com/gotok8s/k8s-docker-desktop-for-mac.git 2、 https://github.com/kubernetes/dashboard 3、 获取token curl ‘http://127.0.0.1:80…

SpringBoot实战项目杂货铺主页统计图表(折线图、饼状图、条形图)

统计图表的制作我们用到了Echarts&#xff0c;不得不说Echarts真的是百度的超级良心产品了。打Call!!!&#x1f44d;&#x1f44d;&#x1f44d; ✔小插曲&#xff1a; 这里博主顺带提一下&#xff0c;像处理访问量等等数据的时候&#xff0c;往往会涉及到一个并发问题。举个…

ADI Blackfin DSP处理器-BF533的开发详解27:扩展IO输出的详细讲解(含源代码)

硬件准备 ADSP-EDU-BF533&#xff1a;BF533开发板 AD-HP530ICE&#xff1a;ADI DSP仿真器 软件准备 Visual DSP软件 硬件链接 硬件设计原理图 功能介绍 ADSP-EDU-BF53x 开发板上扩展接口的 PPORT3 中引出了 4 个扩展 IO 接口输出接口&#xff0c;这些连接到了 CPLD&#x…

【大数据入门核心技术-Hadoop】(八)Hadoop基本管理命令行

目录 一、 三种shell命令方式 二、常见Shell操作命令 三、dfs管理命令行 1、当前haoop环境变量 2、当前集群节点信息 3、运行HTTPFS服务器 4、高可用节点管理 5、单独启动某个节点服务 四、更多命令 一、 三种shell命令方式 HDFS有三种shell命令方式 hadoop fs&#…

flink部署-1.14

1. 版本说明 本文档内容基于 flink-1.14.x&#xff0c;其他版本的整理&#xff0c;请查看本人博客的 flink 专栏其他文章。 2. 概述 Flink 是一种通用性框架&#xff0c;支持多种不同的部署方式。 本章简要介绍 Flink 集群的组成部分、用途和可用实现。如果你只是想在本地启…

线程死锁、锁死、饥饿、活锁讲解

文章目录死锁哲学家就餐问题死锁的检测方式死锁的产生条件死锁的规避死锁的恢复锁死信号丢失锁死嵌套监视器锁死线程饥饿活锁死锁 概念 如果两个或者更多的线程因为相互等待对方而被永远暂停&#xff0c;线程的生命周期变成了BLOCKED或者WAITING&#xff0c;则我们称这些线程产…

计算机-校验码

码距:就单个编码A:00而言&#xff0c;其码距为1&#xff0c;因为其只需要改变一位就变成另一个编码。在两个编码中&#xff0c;从A码到B码转换所需要改变的位数称为码距&#xff0c;如A:00要转换为B:11&#xff0c;码距为2。一般来说&#xff0c;码距越大&#xff0c;越利于纠错…

基于C++实现(控制台)仓库管理系统【100010021】

1题目与要求 1.1问题描述 某电子公司仓库中有若干批次的同一种电脑&#xff0c;按价格、数量来存储。要求实现功能: 初始化 n 批不同价格电脑入库&#xff1b;出库&#xff1a;销售 m 台价格为 p 的电脑&#xff1b;入库&#xff1a;新到 m 台价格为 p 的电脑&#xff1b;盘…

Burp Suite Professional 22.11.4 Crack

Burp Suite Professional 是网络安全测试人员的首选工具包。使用它来自动执行重复的测试任务 - 然后使用其专家设计的手动和半自动安全测试工具进行更深入的挖掘。Burp Suite Professional 可以帮助您测试 OWASP Top 10 漏洞 Burp Suite 被描述为通过 Port Swigger 提供给用户…

Python学习基础笔记三十七——collections模块

1、collections模块&#xff1a; 内置数据类型&#xff1a;列表list、字典dict、集合set、元组tuple。 Collections模块提供了另外的数据类型&#xff1a; 队列deque、双端队列&#xff1a;可以快速地从另外一侧追加和推出元素&#xff1b; namedtuple&#xff1a; 生成可以…

游戏开发53课 阴影

4.8 阴影 阴影的实现方式有很多种&#xff0c;消耗和效果各异。 4.8.1 贴图阴影 贴图的方式最简单&#xff0c;做法是制作一张阴影纹理&#xff0c;放到物体脚下&#xff08;下图&#xff09;&#xff0c;跟随物体一起运动。 贴图阴影渲染非常简单&#xff0c;只需要两个三角…

智能聊天机器人技术研究与应用

文章大纲 1. 聊天机器人简介聊天机器人进化历史聊天机器人核心技术2. 预训练模型与聊天机器人研究进展transfomer 架构回顾预训练对话模型3. 知识图谱与智能问答4. 智能聊天机器人应用实践5. 总结与展望正确使用chatGPT“高端的食材往往只需要最朴素的烹饪方式”参考文献与学习…

OpenFeign使用

OpenFeign使用 在微服务的架构中&#xff0c;传统的http客户端如Httpclient Okhttp HttpURLConnection RestTemplate WebClient 显然不适合。毕竟需要动态的获取服务地址&#xff0c;和进行负载均衡调用。 RPC框架 PC 全称是 Remote Procedure Call &#xff0c;即远程过程调…

NTC-Templates解析与采集H3C(Comware Version 7)信息

本文仅供本人参考与学习NTC-Templates模板使用。 设备环境&#xff1a;HCL S6850 S5820V2-54QS-GE&#xff1b;Version 7.1.075, Alpha 7571 模板采用&#xff1a;hp_comware_display_xxxxxxxx.textfsm 在线模板测试&#xff1a;https://textfsm.nornir.tech/ hp_comware_d…

httpbin的使用

在学习过程中我们想知道我们发送处的http格式是什么样子的&#xff0c;是否符合我们的要求&#xff0c;寻找一个这样的工具&#xff0c;满足验证测试需要。 Httpbin服务可以满足查看我发出去的请求到底是什么样子的。你需要查看请求中的那部分信息&#xff0c;就调用什么样的接…

【Qt入门第37篇】 网络(七)TCP(一)

导语 TCP即TransmissionControl Protocol&#xff0c;传输控制协议。与UDP不同&#xff0c;它是面向连接和数据流的可靠传输协议。也就是说&#xff0c;它能使一台计算机上的数据无差错的发往网络上的其他计算机&#xff0c;所以当要传输大量数据时&#xff0c;我们选用TCP协议…

游戏开发45课 性能优化4

2.6 粒子 粒子特效也是性能的一个大杀手&#xff0c;主要体现在&#xff1a; 每帧计算量大。涉及发射器/效果器/曲线插值等&#xff0c;耗费CPU性能。频繁操作内存。粒子在生命周期里&#xff0c;实例持续不断地创建/删除&#xff0c;即便有缓存机制下&#xff0c;依然避免不…

算法day43|1049,494,474

1049. 最后一块石头的重量 II class Solution:def lastStoneWeightII(self, stones: List[int]) -> int:summ sum(stones)target summ//2#dp下标和数组的定义,dp[j]代表的是最大价值dp [0]*15001#递归公式for i in range(len(stones)):for j in range(target,stones[i]-…

Zero-Shot Learning across Heterogeneous Overlapping Domains

极简论文阅读 摘要 a zero-shot learning approach:零样本学习方法。 natural language understanding domain&#xff1a;自然语言处理域。 a given utterance&#xff1a;给定的话语。 domains at runtime&#xff1a;运行时的域。 utterances and domains 给定话语和域。 …