linux底层使用伙伴系统-buddy管理物理内存,buddy可以被证明是一种很有效的内存管理方式,但是它也拥有很多缺点,其中碎片避免的不完备性--仅仅寄托于释放时的合并操作而不考虑分配时的策略,这也许是它最大的不足,linux2.6内核的后期版本对这个问题进行了改进,大大避免了碎片的泛滥。在linux中,buddy是通过下列数据结构表示的(2.6的早期内核):
struct free_area {
struct list_head free_list;
unsigned long *map;
};
系统中10个free_area组成一个数组,每一个free_area包含一个链表,每一个链表上链接有空闲内存页面块。后来引入了MIGRATE_TYPE,于是free_area结构体就成了:
struct free_area {
struct list_head free_list[MIGRATE_TYPES];
unsigned long nr_free;
};
每一个free_area包含多个链表,其中每一个链表中的内存页面按照其自身是否可以释放或者迁移被归为一类,于是凡是请求“不可迁移”页面的分配请求全部在free_list[MIGRATE_UNMOVABLE]这条链表上分配,和老版本一样,系统中有10个free_area代表大小为2的N次幂个不同页面的集合。这种归类可以最小化内存碎片。
buddy系统本身就有效的防止了碎片,然而还不够。
buddy主要体现在:1.互为buddy的页面块不可能共存于一个free_area链表,它们总是倾向于合并;2.一个free_area的链表中的页面order相同,但是它们肯定彼此不互为buddy,这些页面用于该order大小需求的页面分配。但是buddy的碎片防止机制寄托于内存使用者会及时释放掉内存的情况,如果使用者长期不释放内存,或者说在使用者还没有释放内存的这一段时间期间,碎片将是存在的,并且可能还会导致很大的问题,比如在物理内存中间分配了一页面,然而仅因为分配的这一个页面不可移动,在它被释放之前,系统可用的最大的连续物理内存就只有不到一半物理内存总大小了。究其根源,这种问题的根源在于buddy系统仅仅释放页面时的合并操作防止了碎片的产生,不管页面从哪里被分配,只要它能有效被释放,碎片就是可以避免的,也就是说,buddy系统对于分配并没有更多的约束,仅仅满足在10个free_area中从小到大的顺序扫描即可。
既然找到了buddy的问题,那么只要对分配动作采取一定的约束,碎片就可以进一步避免了。
最简单而又不引入过多复杂性的办法就是将页面按照“可移动”属性分类,将不可移动的页面分为一类,将可以移动的页面分为一类,它们各自占据一块足够大的连续物理空间,不可移动的页面分配需求则尽量在它自己的页面类中分配,可移动的页面也一样,这样一来,不可移动的页面的不可移动性仅仅影响它自身的类别而不会导致一个不可移动的页面两边都是可移动的页面。这就是MIGRATE_TYPE被引入的目的。MIGRATE_TYPE限制了内存页面的分配地点从而避免碎片,而不再仅仅寄希望于它们被释放时通过合并避免碎片。
可以说MIGRATE_TYPE仅仅是一种防止碎片的策略,不应该因为它的存在而影响到内存分配的结果,也就是说,如果在一个MIGRATE_TYPE链表中没有内存可以分配了,那么也还是可以从别的链表中“暂时抢”一些的。另外,还有一个问题,内核载初始化的时候如何为“不可移动类”或者“可移动类”页面指定初始大小呢?也就是说,一开始,系统的free_area中的这些类别链表的页面各该是多少个呢?事实上,内核从来没有指定过初始大小,而是一开始将所有页面都归到“可移动”组当中,而别的组全部都是空的,等到真的有不可移动页面需求的时候再从可移动组中拨一批给不可移动组链表,想一下这也是合理的,毕竟只是一些“不可移动”的页面造成了内存的长期碎片化,如果没有这些长期使用的不可移动页面,碎片的问题是不大的。这个从__rmqueue_fallback函数中可以看出,系统的内存子系统拥有一个fallbacks序列,该序列展示了一个分配序列,也就是如果一个migratetype链表中如果分配不到内存的话,下一个应该在哪个migratetype链表中分配。从__rmqueue_fallback可以看出,如果从要求的migratetype空闲链表中分配不到内存的话,并不是在根据fallbacks序列在“下一个”链表中仅仅分配到自己本次所需的就完事了,而是一次性从fallbacks序列中指示的链表中转移足够多的页面到分配时要求的migratetype链表,毕竟该种类型的空闲链表已经没有页面了,确实需要补充了,并且如果补充的页面太少,那么就会给转移的源migratetype类型组造成碎片,只有一次性分配一大块内存,才不至于引入碎片。
static struct page *__rmqueue_fallback(struct zone *zone, int order,
int start_migratetype)
{
struct free_area * area;
int current_order;
struct page *page;
int migratetype, i;
//尽量一次性拨出尽可能多的内存页面给“该”migratetype的free_area链表
for (current_order = MAX_ORDER-1; current_order >= order; --current_order) {
for (i = 0; i < MIGRATE_TYPES - 1; i++) {
migratetype = fallbacks[start_migratetype][i];
if (migratetype == MIGRATE_RESERVE) //不允许占用保留内存
continue;
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--;
...
list_del(&page->lru);
rmv_page_order(page);
__mod_zone_page_state(zone, NR_FREE_PAGES, -(1UL << order));
if (current_order == pageblock_order)
set_pageblock_migratetype(page, start_migratetype);
//将除去本次自己要用的page[order]之外的其它页面全部补充进该area的migratetype空闲链表
expand(zone, page, order, current_order, area, migratetype);
return page;
}
}
return __rmqueue_smallest(zone, order, MIGRATE_RESERVE);
}
另外,还有一种类似的机制用于避免碎片,那就是使用ZONE的概念,新构造出一个虚拟的ZONE--ZONE_MOVABLE,所谓的虚拟就是它并不和任何物理内存区间相关联,而是可以附着在任何的物理zone上,用户可以通过命令行参数指定用于“可移动”或者“不可移动”的内存的大小,从而也就规定了虚拟的ZONE_MOVABLE的大小。一般的最终比较高的物理内存区域用于可移动的虚拟zone(ZONE_MOVABLE)分配,这是因为低地址内存更多的用于dma或者isa或者内核数据结构(一一线性映射)等,而高内存则一般用于用户进程(可以交换到交换空间...)
分享到:
相关推荐
在Go开发中,`redis-migrate-master`这个文件很可能是项目的主分支源码,包含Go-redis-migrate的全部代码和资源。开发者可以通过阅读源码来理解其内部实现细节,甚至可以根据自己的需求进行定制和扩展。 总的来说,...
《jQuery Migrate Master:深入Windows编程》 在深入探讨jQuery Migrate Master与Windows编程的结合之前,我们首先要理解这两个核心概念。jQuery是一个广泛使用的JavaScript库,它极大地简化了DOM操作、事件处理、...
laravel-migrate-created_at 示例代码如何在 Laravel 4.2 中向 migrates 表添加 created_at 字段 作曲家.json "require": { "sano000/laravel-migrate-created_at": "dev-master" }, "repositories": [ { ...
Redmine是一款开源的项目管理工具,它支持多种版本控制系统,并且拥有丰富的插件体系,能够扩展其功能以适应不同团队的需求。"redmine系统agile敏捷插件安装包"是专门为Redmine系统设计的一个插件,旨在帮助项目团队...
《PyPI官网下载 | raw-sql-migrate-0.1.1.tar.gz》 在Python的世界里,PyPI(Python Package Index)是Python开发者的重要资源库,它为全球的Python项目提供了一个集中发布和下载的地方。这个资源"raw-sql-migrate-...
在jQuery 1.11.3与jQuery Migrate 1.2.1的组合中,开发者可以享受到最新版本的改进,同时又不必担心因移除旧API导致的代码问题。Migrate工具提供了一种平滑的迁移路径,使得项目能够逐步适应新的开发环境,而不必一...
在Laravel框架中,"laravel-tenant-migrate"通常指的是多租户(Tenant)架构下的数据库迁移管理。多租户是一种设计模式,允许在一个单一的应用实例中为多个独立的客户(tenants)提供服务,每个客户都有自己的数据和...
How to Migrate from On-premises to Office 365, https://docs.microsoft.com/zh-cn/sharepointmigration/introducing-the-sharepoint-migration-tool
python-migrate-0.7.2-8.el6.noarch.rpm python-netaddr-0.7.5-4.el6.noarch.rpm python-neutronclient-2.3.4-1.el6.noarch.rpm python-nova-2013.2.3-1.el6.noarch.rpm python-novaclient-2.16.0-2.el6.noarch.rpm ...
在Laravel框架中,"laravel-tenant-migrate"是一个针对多租户应用的扩展,它允许我们在不同的租户环境中独立地运行数据库迁移。多租户架构是为多个客户(或租户)提供服务的一种方式,每个租户都有自己的数据和配置...
<script src="path/to/jquery-migrate-3.0.1.js"> ``` 这里的`path/to`应该替换为实际的文件路径。 **四、迁移策略** 使用jQuery Migrate并不是长久之计,它只是一种过渡手段。开发者应根据控制台的警告信息,...
总之,`jquery-migrate-1.2.1.js` 文件是jQuery生态系统中一个非常实用的工具,它帮助开发者解决版本升级带来的兼容性问题,使得向新版本的迁移过程更加平滑。了解其工作原理和使用方法,对于维护和更新基于jQuery的...
《代码迁移工具:gpt-migrate深度解析》 在当今快速发展的信息技术领域,代码迁移已成为开发者面临的一项常见任务。为了适应技术更新换代或者优化项目结构,开发人员常常需要将代码从一个框架或编程语言迁移到另一...
开源项目-mattes-migrate.zip,Migration file id clash across branches · Issue #45 · mattes/migrate - any comments?
在这个项目中,`laravel-pgsql-multi-tenant-migrate-master`可能是包含整个项目的源代码目录,其中包括了自定义的Laravel artisan命令,这些命令可能被设计用来简化多租户环境中的数据库迁移过程。 在多租户架构中...
pyenv-pip-migrate pyenv-pip-migrate是一个插件,提供了pyenv migrate命令,可将pip软件包从Python版本迁移到另一个版本。 安装 作为pyenv插件安装 将pyenv-pip-migrate安装为pyenv插件将使您可以访问pyenv ...
jquery-migrate-3.3.0 (1).zip
在压缩包内,包含了两个主要文件:`jquery-migrate-3.0.0.js` 和 `jquery-migrate-3.0.0.min.js`。这两个文件分别是未压缩的完整版和压缩后的精简版。未压缩版本方便开发者阅读和调试,而压缩版则用于生产环境,以...
**Laravel开发-fast-migrate** 在 Laravel 框架中,迁移(Migrations)是数据库版本控制的关键组成部分,它允许开发者以结构化的方式管理和更新应用的数据库架构。`fast-migrate` 是一个针对 Laravel 开发的扩展...
这个压缩包文件"laravel-tenant-migrate-master"很可能是该解决方案的一个代码库或者示例项目,用于帮助开发者理解和实现多租户迁移功能。 首先,我们要理解什么是多租户。在软件架构中,多租户是指一个单一实例的...