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.mzph.cn/diannao/6994.shtml

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

相关文章

【企业动态】东胜物联正式加入EnOcean联盟,携手为智慧楼宇及能源管理提供更稳定的硬件解决方案

2024年4月&#xff0c;东胜物联&#xff08;Dusun&#xff09;宣布正式加入EnOcean联盟。EnOcean联盟是一个由来自建筑行业的400多家公司组成的全球性组织。它以EnOcean无线标准&#xff08;ISO/IEC 14543-3-10/11&#xff09;为基础&#xff0c;为可持续建筑自动化建立了创新的…

基于K8S构建Jenkins持续集成平台

文章目录 安装和配置NFSNFS简介NFS安装 在Kubernetes安装Jenkins-Master创建NFS client provisioner安装Jenkins-Master Jenkins与Kubernetes整合实现Jenkins与Kubernetes整合构建Jenkins-Slave自定义镜像 JenkinsKubernetesDocker完成微服务持续集成拉取代码&#xff0c;构建镜…

全栈低代码:前后端业务需求实现100%覆盖!

工具背景&#xff1a; 织信低代码平台“组件设计器”功能专为对个性化定制页面需求较为强烈的用户准备的&#xff0c;该功能组件十分丰富和强大&#xff0c;还融合了AI智能&#xff0c;能够帮助用户0成本起步&#xff0c;平均花1-2个小时就能快速构建一套网站、APP、小程序。 …

期权如何开户的流程是什么样的?

今天期权懂带你了解期权如何开户的流程是什么样的&#xff1f;期权账户开户是指投资者向期权经纪商或金融机构提交申请&#xff0c;以便可以在期权市场上进行交易并持有期权合约的账户开设过程。 期权如何开户的流程是什么样的&#xff1f; 1. 投资者参与营业部提供的股票期权…

Windows环境下VSCode C环境配置

前言&#xff1a; 本文记录了自己在配置 Windows环境下 VSCode C开发环境的遇到的问题和解决方法。 参考: vscode c语言没有代码提示_clangd提示不生效-CSDN博客 VSCODE无法跳转_vscode 不能跳转-CSDN博客 vscode c/c环境配置&#xff08;MinGW&#xff09;调用第三官方库…

【软件工程】测试

目录 前言软件测试的目标测试准则测试方法测试方案&#xff08;重点&#xff09;白盒测试&#xff08;重点&#xff09;逻辑覆盖测试语句覆盖判定覆盖&#xff08;分支覆盖&#xff09;条件覆盖判定/条件覆盖条件组合覆盖总结 基本路径覆盖法 黑盒测试等价类法边界值分析法 软件…

导数和偏导数练习

导数题目列表 偏导数题目列表 这里是上述50个导数和偏导数练习题的答案&#xff1a; 导数答案列表 偏导数答案列表 更多问题咨询 Cos机器人

Linux之命令行参数与环境变量

命令行参数&环境变量 命令行参数 main函数也是一个函数&#xff0c;其实也可以携带参数的 int main( int argc, char *argv[ ], char *envp[ ] ) {program-statements } 那这里是有三个参数的: 第一个参数&#xff1a; argc 是个整型变量&#xff0c;表示命令行参数的个数…

ABAP 第二代增强-采购申请子屏幕增强

文章目录 第二代增强-采购申请子屏幕增强需求实现过程创建项目运行效果客户屏幕的PBO全局变量获取数据更新数据运行效果查询底表修改数据 第二代增强-采购申请子屏幕增强 需求 实现过程 创建项目 运行效果 客户屏幕的PBO 全局变量 *&------------------------------------…

关于Docker的数据管理

文章目录 一、Docker的数据管理1、数据卷1.1 数据卷定义1.2 数据卷配置 2、数据卷容器2.1 创建数据卷容器2.2 使用--volume-from来挂载luck02 二、端口映射三、容器互联1、创建容器互联2、进入luck02测试&#xff08;ping 容器名/别名&#xff09; 四、Docker镜像的创建1、基于…

Java类文件.class详解

一、编译型语言和解释型语言的区别 1、编译型语言&#xff1a; 在编译型语言中&#xff0c;源代码会被整个编译成机器码或者中间代码&#xff08;比如Java的字节码&#xff09;&#xff0c;生成可执行文件。 运行程序时&#xff0c;不需要再对源代码进行解释&#xff0c;而是…

RockChip Android13 NFC SL6320移植

环境:RK3568 Android13 一:驱动移植 1、驱动 将SL6320驱动代码拷贝至kernel-5.10/drivers/misc/sl6320/ 特殊说明:勿将驱动代码放置于kernel-5.10/drivers/nfc/目录下,会导致sl6320驱动生成设备节点时因/dev/nfc节点以创建而加载失败。 2、DTS 本次硬件设计电路走I2C协…

服务器数据恢复—多块磁盘离线导致阵列瘫痪,上层lun不可用的数据恢复案例

服务器存储数据恢复环境&#xff1a; 某品牌MSA2000存储&#xff0c;该存储中有一组由8块SAS硬盘&#xff08;其中有一块热备盘&#xff09;组建的RAID5阵列&#xff0c;raid5阵列上层划分了6个lun&#xff0c;均分配给HP-Unix小型机使用&#xff0c;主要数据为oracle数据库和O…

【LeetCode刷题】34. 在排序数组中查找元素的第一个和最后一个位置

1. 题目链接 34. 在排序数组中查找元素的第一个和最后一个位置 2. 题目描述 3. 解题方法 找到元素的第一个位置&#xff0c;也就是找大于等于目标的最小值找到元素的最后一个位置&#xff0c;也就是找小于等于目标的最大值可以利用2次二分查找来解决 3.1. 第一次查找 3.2. …

今日详解,教你如何不直播在视频号卖货

大家好&#xff0c;我是电商笨笨熊 视频号作为背靠微信的平台&#xff0c;从不需要考虑自身的流量问题&#xff0c; 因此在视频号推出之后就有大批的主播从其他平台转入视频号&#xff1b; 而这时候很多普通人应该也发现了新的机会&#xff0c;不再去内卷抖音、快手直播&…

【Git】Github创建远程仓库并与本地互联

创建仓库 点击生成新的仓库 创建成功后会生成一个这样的文件 拉取到本地 首先先确保本地安装了git 可以通过终端使用 git --version来查看是否安装好了git 如果显示了版本信息&#xff0c;说明已经安装好了git&#xff0c;这时候我们就可以进入我们想要clone到问目标文件夹 …

python-正则表达试-实践1

匹配html标签中的任意标签内数据 匹配所有包含’oo’的单词 import re text "JGood is a handsome boy, he is cool, clever, and so on..." re.findall(r\w*oo\w*, text) 匹配 html中title里面的内容 原文&#xff1a; import re file r./202304.html f open(…

2023数维杯A题原创完整论文思路和求解代码

河流对地下水有着直接地影响,当河流补给地下水时,河流一旦被污染,容易导致地下水以及紧依河流分布的傍河水源地将受到不同程度的污染,这将严重影响工农业的正常运作、社会经济的发展和饮水安全。在地下水污染中最难治理和危害最大的是有机污染,因而对有机污染物在河流-地下…

WordPress Automatic插件 SQL注入漏洞复现(CVE-2024-27956)

0x01 产品简介 WordPress Automatic(又称为WP Automatic)是一款流行的WordPress插件,旨在帮助网站管理员自动化内容创建和发布。该插件可以从各种来源(如RSS Feeds、社交媒体、视频网站、新闻网站等)获取内容,并将其自动发布到WordPress网站。 0x02 漏洞概述 WordPres…

从论文中看AI绘画

个人博客:Sekyoro的博客小屋 个人网站:Proanimer的个人网站 主要看是看Diffusion Models,CLIP,ControlNet,IP-Adapter这种经典论文,尝试总结论文写作的一些方式以及图像生成模型的一些内在思想. 对于其中的数学原理和代码不过深究. DDPM 使用扩散模型得到高质量图像,证明了这…