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,一经查实,立即删除!

相关文章

360的下一代SOC是这个样子的

几乎所有大型企业或机构的IT系统中&#xff0c;都会有安全运营中心(SOC)&#xff0c;它是网络安全防护体系从设备部署到系统建设&#xff0c;再到统一管理&#xff0c;这一发展过程的自然产物。但在国内的实际应用中&#xff0c;SOC的问题多多。 首先是数据类型不全&#xff0c…

【转载】利用scipy.misc等库对jpg以及png等图像数据预处理(用于深度学习喂数据)...

http://blog.csdn.net/qq_16949707/article/details/56306720 转载于:https://www.cnblogs.com/tenderwx/p/8057599.html

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

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

哪一个不是linux常用的shell,Linux下查看使用的是哪种shell的方法汇总

查看当前发行版可以使用的shell复制代码代码如下:[rootlocalhost ~]$ cat /etc/shells/bin/sh/bin/bash/sbin/nologin查看当前使用的shell方法一、最常用的查看shell的命令&#xff0c;但不能实时反映当前shell复制代码代码如下:[rootlocalhost ~]$ echo $SHELL/bin/bash二、下…

企业建设呼叫中心需要考虑哪些因素

呼叫中心发展至今&#xff0c;它的意义早已不是90年代末,只是简单地解决客户客服系统的要求。现在的呼叫中心有了新的使命&#xff0c;比如拓展成为一个信息服务中心&#xff0c;或者成为一个营销中心。客户如何能通过这样的手段&#xff0c;使企业与其他的企业之间形成差异化的…

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

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

linux中使用yum的优点,linux – 自动“yum update”以保证服务器安全 – 优点和缺点?...

这取决于根据我使用CentOS的经验,它非常安全,因为您只使用CentOS基本存储库.您是否应该偶尔尝试更新失败…是…在您应该期望出现故障的硬盘驱动器或偶尔出现故障的CPU的同一级别上.您永远不会有太多备份. &#x1f642;关于自动更新的好处是,您可以比手动修补更快(因此更安全).…

高能力成熟度软件企业中软件质量工程师的职责

随着科学技术的不断发展进步&#xff0c;企业之间的竞争越来越激烈。软件企业要想在竞争中发展生存&#xff0c;提高软件产品质量已成为必要条件。在一些高能力成熟度软件企业中&#xff0c;专门成立了质量保证和控制职能部门&#xff0c;起着提高项目管理透明性和确保软件产品…

存储过程和函数的区别

存储过程和函数的区别存储过程和函数目的是为了 可重复地 执行操作数据库的sql语句的集合。区别是写法和调用上。写法上&#xff1a;存储过程的参数列表可以有输入参数、输出参数、可输入输出的参数&#xff1b;函数的参数列表只有输入参数&#xff0c;并且有return <返回值…

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

点击上方蓝字关注我们&#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…

MySQL—查询某时间范围的数据

-- 查询今天的数据 select * from user where to_days(birthday) to_days(CURDATE()); -- 查询昨天的数据 select * from user where to_days(CURDATE()) - to_days(birthday)<1; -- 查询最近7天的数据 select * from user where birthday > DATE_SUB(CURDATE(),INTERVA…

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…

(转)yi_meng linux 下 ifcfg-eth0 配置 以及ifconfig、ifup、ifdown区别

linux 下 ifcfg-eth0 配置 以及ifconfig、ifup、ifdown区别 原文&#xff1a;https://www.cnblogs.com/yi-meng/p/3214471.html这3个命令的用途都是启动网络接口&#xff0c;不过&#xff0c;ifup与ifdown仅就 /etc/sysconfig/network- scripts内的ifcfg-ethx&#xff08;x为数…

Pycharm上Django的使用 Day8

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