Linux migrate_type初步探索

1、基础知识

我们都知道Linux内存组织管理结构架构,顶层是struct pglist_data,然后再到struct zone,最后是struct page。大概的管理结构是这样的:
在这里插入图片描述
在这里插入图片描述
根据物理内存的地址范围可划分不同的zone,每个zone里的内存由buddy系统所管理,buddy系统管理着不同order大小的链表,在每个不同大小order链表的内部,又根据migrate_type类型进行分类保存。

2、migrate_type作用

为了更好的管理物理内存,操作系统进一步抽象出页块的概念,通常一个页块的大小是2^(MAX_ORDER-1)个页面(4MB)。每个页块对应一个迁移类型migrate_type,buddy系统中的页面,根据其所在migrate_type链表,可知道该页是属于哪个migrate_type的页块。
问: 为什么要抽象出页块,并给页块指定迁移类型呢?
答: 因为要实现页面规整功能。在buddy系统中的页面不断被线程所申请使用,页面外部碎片化就会很严重,很容易就无法分配出连续大order的页面,而且我们也无法进行页面规整,因为我们不知道已分配出的页面是否可以通过将数据迁移到其他页面进行回收。但是当我们有了迁移类型后,我们完全可以知道已分配出的页面数据什么迁移类型,是否支持回收。
例如:当buddy系统中存留page0、page2、page3,page1已经被分配出去,但是page1的所属页块的迁移类型是MIGRATE_MOVABLE,如果我们想用page0-3满足作为order2的分配请求,我们完全可以将page1的数据迁移到page5上,同时再将page1上的映射关系也转移到page5上,这样page1就可以回收回来,与其他page形成order2的页面,满足order2的分配请求。

3、页块的迁移类型存储

我们上面了解到每个页块对应一个迁移类型,这个迁移类型是在哪里存储的呢?另外,如何通过pfn找到对应的页块,进而获取到迁移类型呢?

先明确两个特点:
1、大部分物理内存页面一开始存放在MIGRATE_MOVABLE链表中
2、大部分物理内存页面初始化时存放在order为10的链表中
当我们要使用MIGRATE_UNMOVABLE的页面时,会fallback到MIGRATE_MOVABLE,并将整个页块的迁移类型都改变为MIGRATE_UNMOVABLE

start_kernel()
-> setup_arch()
--> bootmem_init()
---> zone_sizes_init()
----> free_area_init_node()
-----> free_area_init_core()
/** Set up the zone data structures:*   - mark all pages reserved*   - mark all memory queues empty*   - clear the memory bitmaps** NOTE: pgdat should get zeroed by caller.* NOTE: this function is only called during early init.*/
static void __init free_area_init_core(struct pglist_data *pgdat)
{enum zone_type j;int nid = pgdat->node_id;pgdat_init_internals(pgdat);pgdat->per_cpu_nodestats = &boot_nodestats;for (j = 0; j < MAX_NR_ZONES; j++) { // 遍历当前pglist_data所有的zonestruct zone *zone = pgdat->node_zones + j;unsigned long size, freesize, memmap_pages;unsigned long zone_start_pfn = zone->zone_start_pfn;...set_pageblock_order(); // 配置页块大小setup_usemap(pgdat, zone, zone_start_pfn, size); // 设置当前zone内页块的迁移类型保存空间init_currently_empty_zone(zone, zone_start_pfn, size);memmap_init(size, nid, j, zone_start_pfn); // 初始化当前zone}
}

3.1 首先来看一下set_pageblock_order()

/* Initialise the number of pages represented by NR_PAGEBLOCK_BITS */
void __init set_pageblock_order(void)
{unsigned int order;/* Check that pageblock_nr_pages has not already been setup */if (pageblock_order)return;if (HPAGE_SHIFT > PAGE_SHIFT)order = HUGETLB_PAGE_ORDER;elseorder = MAX_ORDER - 1;/** Assume the largest contiguous order of interest is a huge page.* This value may be variable depending on boot parameters on IA64 and* powerpc.*/pageblock_order = order;
}

在没开启HUGETLB_PAGE特性,pageblock_order就为MAX_ORDER-1,也就是10。

3.2 再来看一下setup_usemap()

zone->pageblock_flags 保存当前zone内所有页块的迁移类型信息:

static void __ref setup_usemap(struct pglist_data *pgdat,struct zone *zone,unsigned long zone_start_pfn,unsigned long zonesize)
{// 这里计算要保存zone所有页块对应的迁移类型需要多大的空间unsigned long usemapsize = usemap_size(zone_start_pfn, zonesize);zone->pageblock_flags = NULL;if (usemapsize) {// 为迁移类型块所占空间分配内存zone->pageblock_flags =memblock_alloc_node(usemapsize, SMP_CACHE_BYTES,pgdat->node_id);if (!zone->pageblock_flags)panic("Failed to allocate %ld bytes for zone %s pageblock flags on node %d\n",usemapsize, zone->name, pgdat->node_id);}
}// pageblock_nr_pages表示一个页块包含的页面数量
#define pageblock_nr_pages	(1UL << pageblock_order)/** Calculate the size of the zone->blockflags rounded to an unsigned long* Start by making sure zonesize is a multiple of pageblock_order by rounding* up. Then use 1 NR_PAGEBLOCK_BITS worth of bits per pageblock, finally* round what is now in bits to nearest long in bits, then return it in* bytes.*/
static unsigned long __init usemap_size(unsigned long zone_start_pfn, unsigned long zonesize)
{unsigned long usemapsize;// 这两步主要是进行对齐,得到该zone包含的所有页块的页面总数,对齐按照pageblock_nr_pages去向上取整,将整个zone空间划分成一个个页块大小,不足一个页块的部分,也作为一个页块处理zonesize += zone_start_pfn & (pageblock_nr_pages-1);usemapsize = roundup(zonesize, pageblock_nr_pages);// 得到zone存放的页块总数usemapsize = usemapsize >> pageblock_order;// 每个页块的迁移类型所占空间是NR_PAGEBLOCK_BITS个位,这个宏的值是4,也就是说一个页块的迁移类型需要4个bit来表示usemapsize *= NR_PAGEBLOCK_BITS;// 8表示一个字节包含8个bit,sizeof(unsigned long)表示一个unsigned long类型里有几个字节,其实这里就是计算zone内所有页块的迁移类型需要多大的内存空间(需要多少个unsigned long存储)usemapsize = roundup(usemapsize, 8 * sizeof(unsigned long));// / 8 因为一个unsigned long代表8个字节,所以除以8,转换成所需字节数量return usemapsize / 8;
}

3.3 最后看一下memmap_init()

void __meminit __weak memmap_init(unsigned long size, int nid,unsigned long zone,unsigned long range_start_pfn)
{unsigned long start_pfn, end_pfn;unsigned long range_end_pfn = range_start_pfn + size;int i;// 遍历该zone的合法物理内存区域for_each_mem_pfn_range(i, nid, &start_pfn, &end_pfn, NULL) {start_pfn = clamp(start_pfn, range_start_pfn, range_end_pfn);end_pfn = clamp(end_pfn, range_start_pfn, range_end_pfn);if (end_pfn > start_pfn) {size = end_pfn - start_pfn;// 初始化该区域,并且设置该区域的页块迁移类型是MIGRATE_MOVABLEmemmap_init_zone(size, nid, zone, start_pfn,MEMINIT_EARLY, NULL, MIGRATE_MOVABLE);}}
}/** Initially all pages are reserved - free ones are freed* up by memblock_free_all() once the early boot process is* done. Non-atomic initialization, single-pass.** 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_zone(unsigned long size, int nid, unsigned long zone,unsigned long start_pfn,enum meminit_context context,struct vmem_altmap *altmap, int migratetype)
{unsigned long pfn, end_pfn = start_pfn + size;struct page *page;if (highest_memmap_pfn < end_pfn - 1)highest_memmap_pfn = end_pfn - 1;
...for (pfn = start_pfn; pfn < end_pfn; ) {/** There can be holes in boot-time mem_map[]s handed to this* function.  They do not exist on hotplugged memory.*/if (context == MEMINIT_EARLY) {if (overlap_memmap_init(zone, &pfn))continue;if (defer_init(nid, pfn, end_pfn))break;}// 根据pfn获取到struct page对象page = pfn_to_page(pfn);__init_single_page(page, pfn, zone, nid);if (context == MEMINIT_HOTPLUG)__SetPageReserved(page);/** Usually, we want to mark the pageblock MIGRATE_MOVABLE,* such that unmovable allocations won't be scattered all* over the place during system boot.*/// 如果该pfn是以页块包含页面数量对齐的话if (IS_ALIGNED(pfn, pageblock_nr_pages)) {// 设置迁移类型,该迁移类型是MIGRATE_MOVABLEset_pageblock_migratetype(page, migratetype);cond_resched();}pfn++;}
}void set_pageblock_migratetype(struct page *page, int migratetype)
{if (unlikely(page_group_by_mobility_disabled &&migratetype < MIGRATE_PCPTYPES))migratetype = MIGRATE_UNMOVABLE;set_pfnblock_flags_mask(page, (unsigned long)migratetype,page_to_pfn(page), MIGRATETYPE_MASK);
}/* Return a pointer to the bitmap storing bits affecting a block of pages */
static inline unsigned long *get_pageblock_bitmap(struct page *page,unsigned long pfn)
{
#ifdef CONFIG_SPARSEMEM // 如果开了SPARSMEM布局,则走该路径return section_to_usemap(__pfn_to_section(pfn));
#else // 否则使用zone->pageblock_flagsreturn page_zone(page)->pageblock_flags;
#endif /* CONFIG_SPARSEMEM */
}static inline int pfn_to_bitidx(struct page *page, unsigned long pfn)
{
#ifdef CONFIG_SPARSEMEMpfn &= (PAGES_PER_SECTION-1);
#elsepfn = pfn - round_down(page_zone(page)->zone_start_pfn, pageblock_nr_pages);
#endif /* CONFIG_SPARSEMEM */// 根据pfn获取到所处的页块号,每个页块号对应的迁移类型需要NR_PAGEBLOCK_BITS个bit存储,* NR_PAGEBLOCK_BITS获取到该页块的迁移类型保存的起始bit位置return (pfn >> pageblock_order) * NR_PAGEBLOCK_BITS;
}/*** set_pfnblock_flags_mask - Set the requested group of flags for a pageblock_nr_pages block of pages* @page: The page within the block of interest* @flags: The flags to set* @pfn: The target page frame number* @mask: mask of bits that the caller is interested in*/
void set_pfnblock_flags_mask(struct page *page, unsigned long flags,unsigned long pfn,unsigned long mask)
{unsigned long *bitmap;unsigned long bitidx, word_bitidx;unsigned long old_word, word;BUILD_BUG_ON(NR_PAGEBLOCK_BITS != 4);BUILD_BUG_ON(MIGRATE_TYPES > (1 << PB_migratetype_bits));// 获取zone->pageblock_flags,保存该zone所有页块的迁移类型内存区域bitmap = get_pageblock_bitmap(page, pfn);// 找到该pfn应保存该页块迁移类型的起始bit位置bitidx = pfn_to_bitidx(page, pfn);word_bitidx = bitidx / BITS_PER_LONG;bitidx &= (BITS_PER_LONG-1);VM_BUG_ON_PAGE(!zone_spans_pfn(page_zone(page), pfn), page);mask <<= bitidx;flags <<= bitidx;// 保存迁移类型操作word = READ_ONCE(bitmap[word_bitidx]);for (;;) {old_word = cmpxchg(&bitmap[word_bitidx], word, (word & ~mask) | flags);if (word == old_word)break;word = old_word;}
}

关于migrate_type初步探索先到这里,感谢各位读者浏览!!!
预知后续如何,请看下个博文的分析。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

赋能智慧校园!A3D数字孪生可视化,轻量又高效!

放假之后&#xff0c;学生们会逐步返学&#xff0c;大量人员出入校园&#xff0c;安全更是不容忽视&#xff0c;如何在短时间内对大批人员及设施进行智能监管&#xff1f;数字化转型是关键手段&#xff0c;我们可以融合线上线下数据&#xff0c;搭建3D立体的智慧校园&#xff0…

latex+vscode一直报错,配置文件json和环境变脸配置

1、json配置文件 {"latex-workshop.latex.tools": [{"name": "xelatex","command": "xelatex","args": ["-synctex1","-interactionnonstopmode","-file-line-error","%DOCF…

Hive主要介绍

Hive介绍 hive是基于 Hadoop平台操作 HDFS 文件的插件工具 可以将结构化的数据文件映射为一张数据库表 可以将 HQL 语句转换为 MapReduce 程序 1.hive 是由驱动器组成&#xff0c;驱动器主要由4个组件组成&#xff08;解析器、编译器、优化器、执行器&#xff09; 2.hive本身不…

访问jwt生成token404解决方法

背景&#xff1a; 1.在部署新的阿里云环境后发现调用jwt生成token的方法404&#xff0c;前端除了404&#xff0c;台不报任何错误 在本地好用&#xff0c;在老的阿里云环境好用&#xff0c; 2.缩短生成私钥的参数报错&#xff0c;以为私钥太长改了tomcat参数也无效&#xff0…

《MySQL对库的基本操作》

文章目录 一、查看数据库列表查看数据库中的所有表想知道当前处于哪个数据库里 二、创建一个数据库三、删除一个数据库知道两个集1.字符集2.校验集修改数据库的字符集和编码集 不同的校验码对数据库的影响四、数据库的备份与恢复注意事项&#xff1a;备份数据库中的表 总结 一、…

算法训练营第十三天 | LeetCode 239 滑动窗口最大值、LeetCode 347 前K个高频元素

LeetCode 239 滑动窗口最大值 本体初始思路是这样的&#xff0c;首先看下给定数组长度和维持一个滑动窗口所需要花费的时间复杂度之间的关系。初步判断是还行的&#xff0c;当然后面被样例打脸了。需要更新成优先队列的解法。原本的解法能通过37/51和46/51的测试用例。但这还不…

【Kotlin】Channel简介

1 前言 Channel 是一个并发安全的阻塞队列&#xff0c;可以通过 send 函数往队列中塞入数据&#xff0c;通过 receive 函数从队列中取出数据。 当队列被塞满时&#xff0c;send 函数将被挂起&#xff0c;直到队列有空闲缓存&#xff1b;当队列空闲时&#xff0c;receive 函数将…

python可视化学习笔记折线图问题-起始点问题

问题描述&#xff1a; 起始点的位置不对 from pyecharts.charts import Line import pyecharts.options as opts # 示例数据 x_data [1,2,3,4,5] y_data [1, 2, 3, 4, 5] # 创建 Line 图表 line Line() line.add_xaxis(x_data) line.add_yaxis("test", y_data) li…

基于Hyperf的CMS,企业官网通用php-swoole后台管理系统

2023年9月11日10:47:00 仓库地址&#xff1a; https://gitee.com/open-php/zx-hyperf-cms CMS&#xff0c;企业官网通用PHP后台管理系统 框架介绍 hyperf SCUI 后端开发组件 php 8.1 hyperf 3.1 数据库 sql(使用最新日期文件) hyperf\doc\sql_bak mysql 8. 系统默认账号…

STM32 F103C8T6学习笔记17:类IIC通信—MLX90614红外非接触温度计

今日学习配置MLX90614红外非接触温度计 与 STM32 F103C8T6 单片机的通信 文章提供测试代码讲解、完整工程下载、测试效果图 本文需要用到的大概基础知识&#xff1a;1.3寸OLED配置通信显示、IIC通信、 定时器配置使用 这里就只贴出我的 OLED驱动方面的网址链接了&#xff1a…

ChatGPT4.0知识问答、DALL-E生成AI图片、Code Copilot辅助编程,打开新世界的大门

目录 1、DALL-E 文字转图片 在线AI修改2、Write For Me3、Code Copilot 目前最强的AI编程大模型4、Diagrams: Show Me5、Instant Website [Multipage] 网站合成神器6、AskYourPDF Research Assistant 无限PDF7、Diagrams & Data: Research, Analyze, Visualize 精读Excel …

TCP/IP和HTTP协议

TCP/IP OSI 七层模型在提出时的出发点是基于标准化的考虑&#xff0c;而没有考虑到具体的市场需求&#xff0c;使得该模型结构复杂&#xff0c;部分功能冗余&#xff0c;因而完全实现 OSI 参考模型的系统不多。而 TCP/IP 参考模型直接面向市场需求&#xff0c;实现起来也比较…

LeetCode 543.二叉树的直径

题目描述 给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之间边数表示。 示例 1&#xff1a; 输入&#xff1a;root [1,2,3,4,5]…

报错“Install Js dependencies failed”【鸿蒙开发Bug已解决】

文章目录 项目场景:问题描述原因分析:解决方案:此Bug解决方案总结Bug解决方案寄语项目场景: 最近也是遇到了这个问题,看到网上也有人在询问这个问题,本文总结了自己和其他人的解决经验,解决了【报错“Install Js dependencies failed”】的问题。 报错如下 问题描述 …

【高质量】2024五一数学建模C题保奖思路+代码(后续会更新)

你的点赞收藏是我后续更新的最大动力&#xff01; 一定要点击文末的卡片&#xff0c;那是获取资料的入口&#xff01; 你是否在寻找数学建模比赛的突破点&#xff1f; 作为经验丰富的数学建模团队&#xff0c;我们将为你带来2024 年五一数学建模&#xff08;C题&#xff09;…

复旦微JFM7VX690计算后IO接口模块,用于雷达信号处理、数据处理等需要高速密集计算的应用场景

计算后IO接口模块 1 介绍 1.1 产品概述 计算后IO接口模块主要由复旦微JFM7VX690型FPGA、国产以太网收发器YT8521、国产BMC芯片GD32F450、国产CPLD芯片EF2L45BG256B、国产内存颗粒等主要芯片组成&#xff0c;采用标准6U VPX尺寸设计。 本计算后IO接口模块主要用于雷达信号处…

Nginx负载均衡主备模式

1. 背景 使用Nginx代理后端服务&#xff0c;有时候某些服务是不能使用多台负载均衡&#xff0c;但又想保障高可用&#xff0c;所以采用主备模式&#xff0c;记录如下&#xff1a; 2. 参考 nginx 负载均衡Nginx-负载均衡-后端状态max_conns、down、backup、max_fails、fail_t…

【webrtc】MessageHandler 1: 基于线程的消息处理:以10毫秒处理音频为例

基于m98 G:\CDN\rtcCli\m98\src\audio\null_audio_poller.h分发的消息由MessageHandler 类通过其抽象接口OnMessage 实现处理 NullAudioPoller NullAudioPoller 是一个处理audio的消息的分发器 poll 启动:

2024深圳杯数学建模竞赛A题(东三省数学建模竞赛A题):建立火箭残骸音爆多源定位模型

更新完整代码和成品完整论文 《2024深圳杯&东三省数学建模思路代码成品论文》↓↓↓&#xff08;浏览器打开&#xff09; https://www.yuque.com/u42168770/qv6z0d/zx70edxvbv7rheu7?singleDoc# 2024深圳杯数学建模竞赛A题&#xff08;东三省数学建模竞赛A题&#xff0…

【Go 语言入门专栏】Go 语言的起源与发展

前言 Go 语言是当下最为流行的编程语言之一&#xff0c;大约在 2020、2021 年左右开始于国内盛行&#xff0c;许多大厂很早就将部分 Java 项目迁移到了 Go&#xff0c;足可看出其在性能方面的优越性。 相信各位都知道&#xff0c;在爬虫业务中&#xff0c;并发是一个关键的需…