linux内核对伙伴系统的改进--migrate_type
生活随笔
收集整理的這篇文章主要介紹了
linux内核对伙伴系统的改进--migrate_type
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
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);
}
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或者內核數據結構(一一線性映射)等,而高內存則一般用于用戶進程(可以交換到交換空間...)
?本文轉自 dog250 51CTO博客,原文鏈接:http://blog.51cto.com/dog250/1271151
總結
以上是生活随笔為你收集整理的linux内核对伙伴系统的改进--migrate_type的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL server 数据库危险存储过程
- 下一篇: linux共享上网