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初步探索先到这里,感谢各位读者浏览!!!
预知后续如何,请看下个博文的分析。
相关文章:

Linux migrate_type初步探索
1、基础知识 我们都知道Linux内存组织管理结构架构,顶层是struct pglist_data,然后再到struct zone,最后是struct page。大概的管理结构是这样的: 根据物理内存的地址范围可划分不同的zone,每个zone里的内存由buddy…...

i.MX 6ULL 裸机 IAR 环境安装
一. IAR 的安装请自行搜索 二. 使用最新版本的 IAR,需要修改 SDK 1. 在 SDK 的 core_ca7.h 加上 #include "intrinsics.h" /* IAR Intrinsics */ 2. debug 时需要修改每个工程下的 ddr_init.jlinkscript,参考链接 Solved: How to conn…...

cmake进阶:文件操作
一. 简介 前面几篇文章学习了 cmake的文件操作,写文件,读文件。文章如下: cmake进阶:文件操作之写文件-CSDN博客 cmake进阶:文件操作之读文件-CSDN博客 本文继续学习文件操作。主要学习 文件重命名,删…...

在UI界面中播放视频_unity基础开发教程
在UI界面中播放视频_unity基础开发教程 前言操作步骤结语 前言 之前我写过一篇在场景中播放视频的文章,但是在开发中有时候也会在UI的界面中播放视频,这期我们做一下在UI的界面中播放视频。 操作步骤 首先在场景中创建一个Raw Image,UI->…...
TypeScipt 联合类型 | 号的使用
联合类型有两种使用方法: 一种类型中多个可能的值。具有多种不同的类型中的一种。 一种类型中多个可能的值。 type isAye true | false;const aye:isAye true; const aye1:isAye false; const aye2:isAye 3; // Type number is not assignable to type isAye…...

MATLAB 变换
MATLAB 变换(Transforms) MATLAB提供了用于处理诸如Laplace和Fourier变换之类的变换的命令。转换在科学和工程中用作简化分析和从另一个角度查看数据的工具。 例如,傅立叶变换允许我们将表示为时间函数的信号转换为频率函数。拉普拉斯变换使…...

【005_音频开发_基础篇_ALSA_Codec_驱动-MA120x0P功放】
005_音频开发_基础篇_ALSA_Codec_驱动-MA120x0P功放 文章目录 005_音频开发_基础篇_ALSA_Codec_驱动-MA120x0P功放创作背景MA120X0P输出模式BTLSEPBTLSEBTL 硬件配置方式/硬件Limiter限幅器限幅器作用过程 主要寄存器操作指令 ma120x0p.cma120x0p.h 创作背景 学历代表过去、能…...
2、FreeCAD模块与核心架构总结
FreeCAD作为一个开源的3D建模软件,其内部架构由多个模块组成,这些模块共同协作以支持软件的各种功能。本总结将基于提供的参考文档,对FreeCAD的核心模块、架构特性以及启动过程进行翻译和详细阐述。 核心模块概览 FreeCAD的核心模块主要包括…...
MySQL为什么默认引擎是InnoDB?
因为InnoDB特别强大,其支持很多东西 1.支持事务: 意味着对于一个复杂的SQL语句要么全部执行成功,要么全部失败,因为其底层是原子性的 2.支持并发(行级并发) 意味着面对高并发,多个用户可以同时访问一个表的不同行,不同行之间上锁,而不是给一个表上锁,这样就提高了高并发的性能和…...
K8s: Helm搭建mongodb集群(1)
mongodb 集群搭建 mongdb 部署前 需要创建 pvc, pv 和 sc,如果在云上会自动创建helm 应用中心: https://artifacthub.io 1 )Helm 安装 mongodb A. 无本地存储配置,重启数据消失 在 https://artifacthub.io/packages/helm/bitnami/mongodb…...

应用分层和企业规范
目录 一、应用分层 1、介绍 (1)为什么需要应用分层? (2)如何分层?(三层架构) MVC 和 三层架构的区别和联系 高内聚: 低耦合: 2、代码重构 controlle…...

Flutter笔记:Widgets Easier组件库(1)使用各式边框
Flutter笔记 Widgets Easier组件库(1):使用边框 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite:http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress o…...

OpenHarmony实战开发-上传文件
Web组件支持前端页面选择文件上传功能,应用开发者可以使用onShowFileSelector()接口来处理前端页面文件上传的请求。 下面的示例中,当用户在前端页面点击文件上传按钮,应用侧在onShowFileSelector()接口中收到文件上传请求,在此接…...

外贸企业邮箱是什么?做外贸企业邮箱哪个好?
外贸企业邮箱是什么?外贸企业在进行跨国沟通时必不可少的工具就是外贸企业邮箱,外贸企业邮箱需要具备的条件就是海外邮件抵达率高、安全稳定、多语言沟通。而我们又怎么选择一个适合的外贸企业邮箱呢?小编今天带您一起了解。 一、外贸企业邮…...
写一个简单的程序
思路分析: 1. 导入必要的库 首先,确保你的项目中包含了AWT或Swing库,因为我们将使用它们来创建图形界面。 import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import j…...
CentOS安装Docker指南
Docker安装与配置教程 Docker作为一种轻量级的虚拟化技术,在现代软件开发和运维中扮演着重要的角色。下面,我将以技术博主的身份,向大家详细介绍如何在Linux系统上安装和配置Docker,特别是如何设置Docker的监听地址和端口&#x…...

python绘图(pandas)
matplotlib绘图 import pandas as pd abs_path rF:\Python\learn\python附件\pythonCsv\data.csv df pd.read_csv(abs_path, encodinggbk) # apply根据多列生成新的一个列的操作,用apply df[new_score] df.apply(lambda x : x.数学 x.语文, axis1)# 最后几行 …...

Android(Java)项目支持Kotlin语言开发
Android(Java)项目通过相关Kotlin设置后,允许同时使用Java语言和Kotlin语言进行开发代码的。 示例环境: Android Studio Giraffe | 2022.3.1 Patch 3 Java 8 Kotlin 1.9.20 设置Kotlin选项: 第一步:在项…...
Terraform创建模块
模块就是包含一组Terraform代码的文件夹,可以通过模块直接使用别人编写好的Terraform代码来创建资源。 Terraform模块是编写高质量Terraform代码,提升代码复用性的重要手段,可以说,一个成熟的生产环境应该是由数个可信成熟的模块组…...
《华为鸿蒙:从备胎到主角的崛起之路》
华为鸿蒙操作系统的发展历程可以追溯到 2012 年,当时华为开始规划自有操作系统鸿蒙 OS。然而,直到 2019 年 5 月,鸿蒙才正式进入开发阶段。 2019 年 8 月 9 日,华为正式发布了鸿蒙操作系统。 鸿蒙系统的首个版本是于 2019 年推出…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...

分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...

算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...

JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...