linux内核源码剖析 博客,【Linux内存源码分析】页面迁移

页面迁移其实是伙伴管理算法中的一部分,鉴于其特殊性,特地另行分析。它是2007年的时候,2.6.24内核版本开发时,新增碎片减少策略(the fragmentation reduction strategy)所引入的。该策略也称之为反碎片技术(anti-gragmentation)。

根据《深入linux内核架构》的描述,反碎片的由来是因为Linux内存管理长期存在一个问题:系统启动并长期运行后,物理内存将会产生很多碎片。暂且假设内存页面数为60,则长期运行后,其页面的使用情况可能将会如下图(灰色为已分配)。

8888023d03eb39f029d0eee528bd6a82.png

虽然其未被分配的页面仍有25%,但能够申请到的最大页面仅为一页。不过这对用户空间是没有影响的,主要是由于用户态的内存是通过页面映射而得到的。所以不在乎具体的物理页面分布,其仍是可以将其映射为连续的一块内存提供给用户态程序使用。于是用户态可以感知的内存则如下。

6e58c463a7b5316cb5d91fd73bdcf3e4.png

但是对于内核态,碎片则是个严肃的问题,因为大部分物理内存都直接映射到内核的永久映射区里面。如果真的存在碎片,将真的如第一张图所示,无法映射到比一页更大的内存,这长期是linux的短板之一。于是为了解决该问题,则引入了反碎片。

linux内核在内存管理时,将已分配的页面划分为三种类型:

不可移动页

——该类型页面在内存中位置固定,不可移动。内核核心大部分内存属于该类型;

可回收页

——不能够直接移动,但是可以删除,而内容则可以从某些源重新生成。如文件数据映射的页面则归属此类;

可移动页

——可以随意移动,分配给用户态程序运行的用户空间页面则为该类。由于是通过页面映射而得,将其复制到新位置后,更新映射表项,重新映射,应用程序是不感知的。

页面的可迁移性则取决于它属于哪一类。而内核使用的反碎片技术则是基于将具有相同可移动性的页分组的思想来实现的。当出现碎片的情况时,可移动页面将会迁移,将为申请者腾出所需的连续页面空间,由此避免了空闲页面空间过于零碎而无法申请到大块连续内存。也由此,不可移动页面不允许在可移动页面中申请,避免因不可迁移而导致碎片。

其中具体迁移类型在头文件include/linux/mmzone.h中定义了:

【file:/include/linux/mmzone.h】

enum {

MIGRATE_UNMOVABLE,

MIGRATE_RECLAIMABLE,

MIGRATE_MOVABLE,

MIGRATE_PCPTYPES,/* the number of types on the pcp lists */

MIGRATE_RESERVE = MIGRATE_PCPTYPES,

#ifdef CONFIG_CMA

/*

* MIGRATE_CMA migration type is designed to mimic the way

* ZONE_MOVABLE works. Only movable pages can be allocated

* from MIGRATE_CMA pageblocks and page allocator never

* implicitly change migration type of MIGRATE_CMA pageblock.

*

* The way to use it is to change migratetype of a range of

* pageblocks to MIGRATE_CMA which can be done by

* __free_pageblock_cma() function. What is important though

* is that a range of pageblocks must be aligned to

* MAX_ORDER_NR_PAGES should biggest page be bigger then

* a single pageblock.

*/

MIGRATE_CMA,

#endif

#ifdef CONFIG_MEMORY_ISOLATION

MIGRATE_ISOLATE,/* can't allocate from here */

#endif

MIGRATE_TYPES

};

各类型的说明则为:

MIGRATE_UNMOVABLE

——在内存当中有固定的位置,不能移动。内核的核心分配的内存大多属于这种类型;

MIGRATE_RECLAIMABLE

——不能直接移动,但可以删除,其内容页可以从其他地方重新生成,例如,映射自文件的数据属于这种类型,针对这种页,内核有专门的页面回收处理;

MIGRATE_MOVABLE

——可以随意移动,用户空间应用程序所用到的页属于该类别。它们通过页表来映射,如果他们复制到新的位置,页表项也会相应的更新,应用程序不会注意到任何改变;

MIGRATE_PCPTYPES

——是per_cpu_pageset,即用来表示每CPU页框高速缓存的数据结构中的链表的迁移类型数目;

MIGRATE_RESERVE

——保留页,是在前三种的列表中都没用可满足分配的内存块时,就可以从MIGRATE_RESERVE分配;

MIGRATE_CMA

——连续内存分配,用于避免预留大块内存导致系统可用内存减少而实现的,即当驱动不使用内存时,将其分配给用户使用,而需要时则通过回收或者迁移的方式将内存腾出来。

MIGRATE_ISOLATE

——用于跨越NUMA节点移动物理内存页,该索引的页不能分配,在大型系统上,它有益于将物理内存页移动到接近于是用该页最频繁地CPU;

MIGRATE_TYPES

——表示迁移类型的数目。

至于迁移类型的页面管理实际上采用的还是伙伴管理算法的管理方式,内存管理区zone的结构里面的free_area是用于管理各阶内存页面,而其里面的free_list则是对各迁移类型进行区分的链表。回顾内存页面释放的函数__free_pages,其将空闲页面挂回去的时候,是做了迁移类型区分的。也就是意味着页面迁移类型是伴随着伙伴管理算法的内存管理构建,根据迁移类型进行分而治之初始化。

那么各种迁移类型的页面分配是如何运转的?

页面分配函数入口__alloc_pages():

【file:/include/linux/gfp.h】

static inline struct page *

__alloc_pages(gfp_t gfp_mask, unsigned int order,

struct zonelist *zonelist)

{

return __alloc_pages_nodemask(gfp_mask, order, zonelist, NULL);

}

其首入参在__alloc_pages_nodemask()里面会经过allocflags_to_migratetype(gfp_mask)转换获取到申请页面的类型。该迁移类型会在其内部调用函数__rmqueue()中使用。

【file:/mm/page_alloc.c】

/*

* Do the hard work of removing an element from the buddy allocator.

* Call me with the zone->lock already held.

*/

static struct page *__rmqueue(struct zone *zone, unsigned int order,

int migratetype)

{

struct page *page;

retry_reserve:

page = __rmqueue_smallest(zone, order, migratetype);

if (unlikely(!page) && migratetype != MIGRATE_RESERVE) {

page = __rmqueue_fallback(zone, order, migratetype);

/*

* Use MIGRATE_RESERVE rather than fail an allocation. goto

* is used because __rmqueue_smallest is an inline function

* and we want just one call site

*/

if (!page) {

migratetype = MIGRATE_RESERVE;

goto retry_reserve;

}

}

trace_mm_page_alloc_zone_locked(page, order, migratetype);

return page;

}

前面分析可以知道__rmqueue_smallest()仅是在指定迁移类型下自底向上进行各阶遍历查找所需的空闲页面,而据上代码其如果在指定迁移类型下分配失败,且类型不为MIGRATE_RESERVE时,将会调用__rmqueue_fallback()进行分配。

接下来看一下__rmqueue_fallback()实现:

【file:/mm/page_alloc.c】

/* Remove an element from the buddy allocator from the fallback list */

static inline struct page *

__rmqueue_fallback(struct zone *zone, int order, int start_migratetype)

{

struct free_area *area;

int current_order;

struct page *page;

int migratetype, new_type, i;

/* Find the largest possible block of pages in the other list */

for (current_order = MAX_ORDER-1; current_order >= order;

--current_order) {

for (i = 0;; i++) {

migratetype = fallbacks[start_migratetype][i];

/* MIGRATE_RESERVE handled later if necessary */

if (migratetype == MIGRATE_RESERVE)

break;

area = &(zone->free_area[current_order]);

if (list_empty(&area->free_list[migratetype]))

continue;

page = list_entry(area->free_list[migratetype].next,

struct page, lru);

area->nr_free--;

new_type = try_to_steal_freepages(zone, page,

start_migratetype,

migratetype);

/* Remove the page from the freelists */

list_del(&page->lru);

rmv_page_order(page);

expand(zone, page, order, current_order, area,

new_type);

trace_mm_page_alloc_extfrag(page, order, current_order,

start_migratetype, migratetype, new_type);

return page;

}

}

return NULL;

}

可以看到其异于通常的伙伴管理算法,内存页面是由最高阶开始进行查找的,而查找的迁移类型是根据fallbacks备选类型中进行遍历获得并止于MIGRATE_RESERVE类型。由此获得的阶号和迁移类型查找zone->free_area[]->free_list[]空闲页面管理链表,如果查找到的话,则将其摘除,否则进入下一类型查找,最后所有类型都查找不到的时候,才会降阶查找。

其中fallbacks[][]是已确定的类型顺序结构,其定义为:

【file:/mm/page_alloc.c】

/*

* This array describes the order lists are fallen back to when

* the free lists for the desirable migrate type are depleted

*/

static int fallbacks[MIGRATE_TYPES][4] = {

[MIGRATE_UNMOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE },

[MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE },

#ifdef CONFIG_CMA

[MIGRATE_MOVABLE] = { MIGRATE_CMA, MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },

[MIGRATE_CMA] = { MIGRATE_RESERVE }, /* Never used */

#else

[MIGRATE_MOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },

#endif

[MIGRATE_RESERVE] = { MIGRATE_RESERVE }, /* Never used */

#ifdef CONFIG_MEMORY_ISOLATION

[MIGRATE_ISOLATE] = { MIGRATE_RESERVE }, /* Never used */

#endif

};

具体分析一下try_to_steal_freepages()函数实现:

【file:/mm/page_alloc.c】

/*

* If breaking a large block of pages, move all free pages to the preferred

* allocation list. If falling back for a reclaimable kernel allocation, be

* more aggressive about taking ownership of free pages.

*

* On the other hand, never change migration type of MIGRATE_CMA pageblocks

* nor move CMA pages to different free lists. We don't want unmovable pages

* to be allocated from MIGRATE_CMA areas.

*

* Returns the new migratetype of the pageblock (or the same old migratetype

* if it was unchanged).

*/

static int try_to_steal_freepages(struct zone *zone, struct page *page,

int start_type, int fallback_type)

{

int current_order = page_order(page);

/*

* When borrowing from MIGRATE_CMA, we need to release the excess

* buddy pages to CMA itself.

*/

if (is_migrate_cma(fallback_type))

return fallback_type;

/* Take ownership for orders >= pageblock_order */

if (current_order >= pageblock_order) {

change_pageblock_range(page, current_order, start_type);

return start_type;

}

if (current_order >= pageblock_order / 2 ||

start_type == MIGRATE_RECLAIMABLE ||

page_group_by_mobility_disabled) {

int pages;

pages = move_freepages_block(zone, page, start_type);

/* Claim the whole block if over half of it is free */

if (pages >= (1 << (pageblock_order-1)) ||

page_group_by_mobility_disabled) {

set_pageblock_migratetype(page, start_type);

return start_type;

}

}

return fallback_type;

}

该函数主要实现了内存页面的迁移类型的变更,将__rmqueue_fallback()查找到满足需要的内存页面空间类型转为申请的类型。其中MIGRATE_CMA类型不做类型转换,此外类型转换的页面数量为pageblock_nr_pages为单位的倍数,还有就是对于阶较低的内存页面(小于pageblock_order/2)、类型不为MIGRATE_RECLAIMABLE且未开启页面迁移的情况下,是不做类型转换的。完了,在__rmqueue_fallback()里面根据其转换后的类型通过expand()扩展到对应的迁移类型伙伴管理系统中。

小结一下,__rmqueue_fallback()是自高往低阶遍历fallbacks迁移类型表,查找满足分配需要的内存页面,然后将查找到的内存页面进行类型变更后合并到所申请的类型中,以实现类型迁移。值得注意的是,之所以内存迁移都是以内存块的高阶进行的,主要就是为了反碎片化,避免当前类型无法满足需要的时候,过于频繁地向备选类型进行小片内存申请和做迁移而导致备选类型的内存页面产生大量水平,将问题控制在所申请的内存类型中。

最后看一下set_pageblock_migratetype()的实现:

【file:/mm/page_alloc.c】

/**

* set_pageblock_flags_mask - Set the requested group of flags for a pageblock_nr_pages block of pages

* @page: The page within the block of interest

* @start_bitidx: The first bit of interest

* @end_bitidx: The last bit of interest

* @flags: The flags to set

*/

void set_pageblock_flags_mask(struct page *page, unsigned long flags,

unsigned long end_bitidx,

unsigned long mask)

{

struct zone *zone;

unsigned long *bitmap;

unsigned long pfn, bitidx, word_bitidx;

unsigned long old_word, word;

BUILD_BUG_ON(NR_PAGEBLOCK_BITS != 4);

zone = page_zone(page);

pfn = page_to_pfn(page);

bitmap = get_pageblock_bitmap(zone, pfn);

bitidx = pfn_to_bitidx(zone, pfn);

word_bitidx = bitidx / BITS_PER_LONG;

bitidx &= (BITS_PER_LONG-1);

VM_BUG_ON_PAGE(!zone_spans_pfn(zone, pfn), page);

bitidx += end_bitidx;

mask <<= (BITS_PER_LONG - bitidx - 1);

flags <<= (BITS_PER_LONG - bitidx - 1);

word = ACCESS_ONCE(bitmap[word_bitidx]);

for (;;) {

old_word = cmpxchg(&bitmap[word_bitidx], word, (word & ~mask) | flags);

if (word == old_word)

break;

word = old_word;

}

}

其中get_pageblock_bitmap()用于取得zone结构体中pageblock_flags成员,而后面则是基于此做位图操作。通过该函数,可以看到内存页面的类型管理是通过其所属的zone的结构体中的pageblock_flags所管理的位图进行标识该页面的迁移类型。

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

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

相关文章

2018年下半年网络公式考试案例分析真题

阅读以下说明&#xff0c;回答问题1至问题3&#xff0c;将解答填入答题纸对应的解答栏内。【说明】某公司网络划分为两个子网&#xff0c;其中设备A是DHCP服务器&#xff0c;如图3-1所示。 【问题1】(6分&#xff0c;每空2分)DHCP在分配IP地址时使用 (1) 的方式&#xff0c; 而…

【单片机入门】(三)应用层软件开发的单片机学习之路-----UART串口通讯和c#交互...

本文由网友投稿。作者&#xff1a;陈显达原文标题&#xff1a;【单片机入门】(三)应用层软件开发的单片机学习之路-----UART串口通讯和c#交互原文链接&#xff1a;https://www.cnblogs.com/1996-Chinese-Chen/p/16826558.html引言在第一章博客中&#xff0c;我们讲了Arduino对E…

机器学习案例丨基于广泛和深入的推荐 - 餐厅评级预测

点击上方蓝字关注我们&#xff08;本文阅读时间&#xff1a;18分钟&#xff09;Microsoft Azure Machine Learning Studio 是微软强大的机器学习平台&#xff0c;在设计器中&#xff0c;微软内置了15个场景案例&#xff0c;但网上似乎没有对这15个案例深度刨析的分析资料&#…

css linux 等宽字体,比例字体等宽字体

我们都知道等宽字体和比例字体的区别&#xff0c;就在于比例字体(Monospaced Font)即每个字母宽度是按一定比例自动调整的&#xff0c;而等宽字体(Proportional font)则是固定宽度&#xff0c;固定间距&#xff0c;字体的每一个字母和字符所占的水平空间都是相同的。比例字体&a…

三星智能家居系统频繁故障 大批用户受到影响

Shelley Powers正在她密苏里州郊区的房子中鼾睡&#xff0c;突然警铃大作将她惊醒&#xff0c;在仔细排查之后&#xff0c;发现是虚惊一场&#xff0c;是安全传感器误报。在此之后的几周&#xff0c;她的三星Smart Things智能家居系统频繁出现故障&#xff0c;比如设定的灯具不…

适用于 .NET 的开源文本差异对比组件

你好&#xff0c;这里是 Dotnet 工具箱&#xff0c;定期分享 Dotnet 有趣&#xff0c;实用的工具或组件&#xff0c;希望对您有用&#xff01;简介对于开发人员来说&#xff0c;Git 是我们经常使用的工具&#xff0c;在每次编写完代码并提交后&#xff0c;我们可以通过 git dif…

box-shadow阴影合集

2019独角兽企业重金招聘Python工程师标准>>> * box-shadow可以设置6个值。其中4个可选&#xff1b;2个必须指定&#xff1a;分别是x轴偏移量和y轴偏移量&#xff0c;这2个值可以是正值&#xff0c;可以是负值&#xff0c;也可以是0&#xff0c;但不可以省略不写 阴影…

《数据科学R语言实践:面向计算推理与问题求解的案例研究法》一一2.5 为跨年度的个人参赛选手构造记录...

本节书摘来自华章计算机《数据科学R语言实践&#xff1a;面向计算推理与问题求解的案例研究法》一书中的第2章&#xff0c;第2.5节,作者&#xff1a;[美] 德博拉诺兰&#xff08;Deborah Nolan&#xff09;  邓肯坦普朗&#xff08;Duncan Temple Lang&#xff09;  更多章…

基于 abp 微服务架构的开源低代码平台

你好&#xff0c;这里是 Dotnet 工具箱&#xff0c;定期分享 Dotnet 有趣&#xff0c;实用的工具或组件&#xff0c;希望对您有用&#xff01;简介 ABP-MicroService是 一个基于ABP vNext微服务架构、vue-element-admin的后台管理框架&#xff0c;适用于大型分布式业务系统和企…

linux android sdk gengxinman,Android 实现增量更新

一、概述增量更新相较于全量更新的好处不言而喻&#xff0c;利用差分算法获得1.0版本到2.0版本的差分包&#xff0c;这样在安装了1.0的设备上只要下载这个差分包就能够完成由1.0-2.0的更新。比如&#xff1a;存在一个1.0版本的apkapk1.png然后需要升级到2.0版本&#xff0c;而2…

Pycharm上Django的使用 Day8

2019独角兽企业重金招聘Python工程师标准>>> 1.添加新条目 1>编写用于添加新条目的表单 在forms.py中创建一个与模型Entry相关联的表单 1处给字段text指定一个空标签 2处定义小部件widgets,widgets是一个HTML表单元素 2>定义new_entry的URL模式 在用于添加新条…

物联网创新领域的三大驱动性趋势

从设备对接到区块链&#xff0c;今天我们将介绍几项物联网开发者必须了解的最新技术趋势。 物联网正在快速改变着我们的工作与生活方式。过去十年以来&#xff0c;利用惟一IP地址实现联网早已不是难事&#xff0c;但传感器、处理器以及其它多种新兴技术的涌现真正让物联网走向了…

TensorFlow.NET 实战 -- 为.NET开发者开启机器学习之旅

在IT技术飞速发展的今天&#xff0c;云计算、人工智能、大数据和云原生应用等新兴技术的发展为我们带来一波又一波的浪潮&#xff0c;也对软件开发者提出了更高的要求&#xff0c;特别是人工智能开发领域&#xff0c;应运而生的新概念让人目不暇接。作为一个.NET开发者的你&…

Arduino I/O Expansion Shield V7.1

介绍&#xff1a; 传感器扩展板&#xff0c;目的是为初学者省去繁琐的面包板接线和故障排除&#xff0c;专注实现自己的创意想法。 扩展板V7.1不仅保留老版本的所有优点&#xff0c;改善了它的易用性&#xff0c;更重要的是它兼容Arduino新一代主控器DUE。现在V7.1以其强大的扩…

简单的单臂路由的配置实验 (思科)

实验名称&#xff1a;简单的单臂路由的 配置实验 &#xff08;思科&#xff09;实验拓扑&#xff1a; 实验需要&#xff1a;1、按图中所示配置设网络备vlan&#xff0c;IP地址2、能够使各pc互相ping通。 实验步骤&#xff1a; 1、配置终端设备&#xff1a;pc1-pc5按照拓扑图中所…

【Redis源码分析】Redis命令处理生命周期

运营研发团队 李乐 前言 本文主要讲解服务器处理客户端命令请求的整个流程&#xff0c;包括服务器启动监听&#xff0c;接收命令请求并解析&#xff0c;执行命令请求&#xff0c;返回命令回复等&#xff0c;这也是本文的主题“命令处理的生命周期”。 Redis服务器作为典型的事件…

GDB调试qemu-kvm

GDB调试qemu-kvm 前面几篇博文都是记录一些kvm相关包编译安装及使用&#xff0c;但都没深入去代码看看。看源码在配合上相关原理才能更好的理解kvm。但qemu-kvm的代码量很多&#xff0c;对我来讲直接看源码收获甚少&#xff0c;所以找了个调试工具——GDB来配合阅读代码。接下来…

用.NET设计一个假装黑客的屏幕保护程序

本文主要介绍屏幕保护程序的一些相关知识&#xff0c;以及其在安全方面的用途&#xff0c;同时介绍了如何使用 .NET 开发一款屏幕保护程序&#xff0c;并对核心功能做了介绍&#xff0c;案例代码开源&#xff1a;https://github.com/sangyuxiaowu/HackerScreenSaver背景前几天在…

【IntelliJ】IntelliJ IDEA常用设置及快捷键以及自定义Live templates

IntelliJ IDEA是一款非常优秀的JAVA编辑器&#xff0c;初学都可会对其中的一些做法感到很别扭&#xff0c;刚开始用的时候我也感到很不习惯&#xff0c;在参考了网上一些文章后在这里把我的一些经验写出来&#xff0c;希望初学者能快速适应它&#xff0c;不久你就会感觉到编程是…

c语言时钟报告,C语言图形时钟课程设计实验报告

C语言图形时钟课程设计实验报告 目录1.系统功能要求。2. 数据结构设计及说明。3.程序结构(画流程图) 。4.各模块的功能。5.试验结果(包括输入数据和输出结果) 。6.体会。7.参考文献。8.附录&#xff1a;程序清单及源程序。 系统功能要求&#xff1a;在屏幕上显示一个图形时钟…