linux 下的csp 模块,linux kernel 中MIGRATE_TYPES的理解
在2.6.24之前的內(nèi)核版本中,free_area結(jié)構(gòu)中只有一個free_list數(shù)組,而從2.6.24開始,free_area結(jié)構(gòu)中存有MIGRATE_TYPES個free_list,這些數(shù)組是根據(jù)頁框的移動性來劃分的,為什么要進行這樣的劃分呢?實際上也是為了減少碎片而提出的,我們考慮下面的情況:
圖中一共有32個頁,只分配出了4個頁框,但是能夠分配的最大連續(xù)內(nèi)存也只有8個頁框(因為伙伴系統(tǒng)分配出去的內(nèi)存必須是2的整數(shù)次冪個頁框),內(nèi)核解決這種問題的辦法就是將不同類型的頁進行分組。分配出去的頁面可分為三種類型:
不可移動頁(Non-movable pages):這類頁在內(nèi)存當(dāng)中有固定的位置,不能移動。內(nèi)核的核心分配的內(nèi)存大多屬于這種類型
可回收頁(Reclaimable pages):這類頁不能直接移動,但可以刪除,其內(nèi)容頁可以從其他地方重新生成,例如,映射自文件的數(shù)據(jù)屬于這種類型,針對這種頁,內(nèi)核有專門的頁面回收處理
可移動頁(Movable pages):這類頁可以隨意移動,用戶空間應(yīng)用程序所用到的頁屬于該類別。它們通過頁表來映射,如果他們復(fù)制到新的位置,頁表項也會相應(yīng)的更新,應(yīng)用程序不會注意到任何改變。
假如上圖中大部分頁都是可移動頁,而分配出去的四個頁都是不可移動頁,由于不可移動頁插在了其他類型頁的中間,就導(dǎo)致了無法從原本空閑的連續(xù)內(nèi)存區(qū)中分配較大的內(nèi)存塊。考慮下圖的情況:
將可回收頁和不可移動頁分開,這樣雖然在不可移動頁的區(qū)域當(dāng)中無法分配大塊的連續(xù)內(nèi)存,但是可回收頁的區(qū)域卻沒有受其影響,可以分配大塊的連續(xù)內(nèi)存。
內(nèi)核對于遷移類型的定義如下:
[cpp]
#define MIGRATE_UNMOVABLE ? ? 0
#define MIGRATE_RECLAIMABLE ? 1
#define MIGRATE_MOVABLE ? ? ? 2
#define MIGRATE_PCPTYPES ? ? ?3 /* the number of types on the pcp lists */
#define MIGRATE_RESERVE ? ? ? 3
#define MIGRATE_ISOLATE ? ? ? 4 /* can't allocate from here */
#define MIGRATE_TYPES ? ? ? ? 5
前三種類型已經(jīng)介紹過
MIGRATE_PCPTYPES是per_cpu_pageset,即用來表示每CPU頁框高速緩存的數(shù)據(jù)結(jié)構(gòu)中的鏈表的遷移類型數(shù)目
MIGRATE_RESERVE是在前三種的列表中都沒用可滿足分配的內(nèi)存塊時,就可以從MIGRATE_RESERVE分配
MIGRATE_ISOLATE用于跨越NUMA節(jié)點移動物理內(nèi)存頁,在大型系統(tǒng)上,它有益于將物理內(nèi)存頁移動到接近于是用該頁最頻繁地CPU
MIGRATE_TYPES表示遷移類型的數(shù)目
當(dāng)一個指定的遷移類型所對應(yīng)的鏈表中沒有空閑塊時,將會按以下定義的順序到其他遷移類型的鏈表中尋找:
static int fallbacks[MIGRATE_TYPES][MIGRATE_TYPES-1] = {
[MIGRATE_UNMOVABLE] ? = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE, ? MIGRATE_RESERVE },
[MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE, ? MIGRATE_MOVABLE, ? MIGRATE_RESERVE },
[MIGRATE_MOVABLE] ? ? = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },
[MIGRATE_RESERVE] ? ? = { MIGRATE_RESERVE, ? ? MIGRATE_RESERVE, ? MIGRATE_RESERVE }, /* Never used */
};
(三)?linux內(nèi)核對伙伴系統(tǒng)的改進--migrate_type
linux底層使用伙伴系統(tǒng)-buddy管理物理內(nèi)存,buddy可以被證明是一種很有效的內(nèi)存管理方式,但是它也擁有很多缺點,其中碎片避免的不完備性 --僅僅寄托于釋放時的合并操作而不考慮分配時的策略,這也許是它最大的不足,linux2.6內(nèi)核的后期版本對這個問題進行了改進,大大避免了碎片的泛 濫。在linux中,buddy是通過下列數(shù)據(jù)結(jié)構(gòu)表示的(2.6的早期內(nèi)核):
struct free_area {
struct list_head??? free_list;
unsigned long??? ??? *map;
};
系統(tǒng)中10個free_area組成一個數(shù)組,每一個free_area包含一個鏈表,每一個鏈表上鏈接有空閑內(nèi)存頁面塊。后來引入了MIGRATE_TYPE,于是free_area結(jié)構(gòu)體就成了:
struct free_area {
struct list_head??? free_list[MIGRATE_TYPES];
unsigned long??? ??? nr_free;
};
每一個free_area包含多個鏈表,其中每一個鏈表中的內(nèi)存頁面按照其自身是否可以釋放或者遷移被歸為一類,于是凡是請求“不可遷移”頁面的分配請求 全部在free_list[MIGRATE_UNMOVABLE]這條鏈表上分配,和老版本一樣,系統(tǒng)中有10個free_area代表大小為2的N次冪 個不同頁面的集合。這種歸類可以最小化內(nèi)存碎片。
buddy系統(tǒng)本身就有效的防止了碎片,然而還不夠。
buddy主要體現(xiàn)在:1.互為buddy的頁面塊不可能共存于一個free_area鏈表,它們總是傾向于合并;2.一個free_area的鏈表中的 頁面order相同,但是它們肯定彼此不互為buddy,這些頁面用于該order大小需求的頁面分配。但是buddy的碎片防止機制寄托于內(nèi)存使用者會 及時釋放掉內(nèi)存的情況,如果使用者長期不釋放內(nèi)存,或者說在使用者還沒有釋放內(nèi)存的這一段時間期間,碎片將是存在的,并且可能還會導(dǎo)致很大的問題,比如在 物理內(nèi)存中間分配了一頁面,然而僅因為分配的這一個頁面不可移動,在它被釋放之前,系統(tǒng)可用的最大的連續(xù)物理內(nèi)存就只有不到一半物理內(nèi)存總大小了。究其根 源,這種問題的根源在于buddy系統(tǒng)僅僅釋放頁面時的合并操作防止了碎片的產(chǎn)生,不管頁面從哪里被分配,只要它能有效被釋放,碎片就是可以避免的,也就 是說,buddy系統(tǒng)對于分配并沒有更多的約束,僅僅滿足在10個free_area中從小到大的順序掃描即可。
既然找到了buddy的問題,那么只要對分配動作采取一定的約束,碎片就可以進一步避免了。
最簡單而又不引入過多復(fù)雜性的辦法就是將頁面按照“可移動”屬性分類,將不可移動的頁面分為一類,將可以移動的頁面分為一類,它們各自占據(jù)一塊足夠大的連 續(xù)物理空間,不可移動的頁面分配需求則盡量在它自己的頁面類中分配,可移動的頁面也一樣,這樣一來,不可移動的頁面的不可移動性僅僅影響它自身的類別而不 會導(dǎo)致一個不可移動的頁面兩邊都是可移動的頁面。這就是MIGRATE_TYPE被引入的目的。MIGRATE_TYPE限制了內(nèi)存頁面的分配地點從而避 免碎片,而不再僅僅寄希望于它們被釋放時通過合并避免碎片。
可以說MIGRATE_TYPE僅僅是一種防止碎片的策略,不應(yīng)該因為它的存在而影響到內(nèi)存分配的結(jié)果,也就是說,如果在一個MIGRATE_TYPE鏈 表中沒有內(nèi)存可以分配了,那么也還是可以從別的鏈表中“暫時搶”一些的。另外,還有一個問題,內(nèi)核載初始化的時候如何為“不可移動類”或者“可移動類”頁 面指定初始大小呢?也就是說,一開始,系統(tǒng)的free_area中的這些類別鏈表的頁面各該是多少個呢?事實上,內(nèi)核從來沒有指定過初始大小,而是一開始 將所有頁面都歸到“可移動”組當(dāng)中,而別的組全部都是空的,等到真的有不可移動頁面需求的時候再從可移動組中撥一批給不可移動組鏈表,想一下這也是合理 的,畢竟只是一些“不可移動”的頁面造成了內(nèi)存的長期碎片化,如果沒有這些長期使用的不可移動頁面,碎片的問題是不大的。這個從 __rmqueue_fallback函數(shù)中可以看出,系統(tǒng)的內(nèi)存子系統(tǒng)擁有一個fallbacks序列,該序列展示了一個分配序列,也就是如果一個 migratetype鏈表中如果分配不到內(nèi)存的話,下一個應(yīng)該在哪個migratetype鏈表中分配。從__rmqueue_fallback可以看 出,如果從要求的migratetype空閑鏈表中分配不到內(nèi)存的話,并不是在根據(jù)fallbacks序列在“下一個”鏈表中僅僅分配到自己本次所需的就 完事了,而是一次性從fallbacks序列中指示的鏈表中轉(zhuǎn)移足夠多的頁面到分配時要求的migratetype鏈表,畢竟該種類型的空閑鏈表已經(jīng)沒有 頁面了,確實需要補充了,并且如果補充的頁面太少,那么就會給轉(zhuǎn)移的源migratetype類型組造成碎片,只有一次性分配一大塊內(nèi)存,才不至于引入碎 片。
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;
//盡量一次性撥出盡可能多的內(nè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)? //不允許占用保留內(nèi)存
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的概念,新構(gòu)造出一個虛擬的ZONE--ZONE_MOVABLE,所謂的虛擬就是它并不和任 何物理內(nèi)存區(qū)間相關(guān)聯(lián),而是可以附著在任何的物理zone上,用戶可以通過命令行參數(shù)指定用于“可移動”或者“不可移動”的內(nèi)存的大小,從而也就規(guī)定了虛 擬的ZONE_MOVABLE的大小。一般的最終比較高的物理內(nèi)存區(qū)域用于可移動的虛擬zone(ZONE_MOVABLE)分配,這是因為低地址內(nèi)存更 多的用于dma或者isa或者內(nèi)核數(shù)據(jù)結(jié)構(gòu)(一一線性映射)等,而高內(nèi)存則一般用于用戶進程(可以交換到交換空間...)
總結(jié)
以上是生活随笔為你收集整理的linux 下的csp 模块,linux kernel 中MIGRATE_TYPES的理解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [2] ADB 基本用法
- 下一篇: 计算机控制作业及答案,《微机系统与维护》