日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

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

發(fā)布時間:2023/12/4 linux 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux内核源码剖析 博客,【Linux内存源码分析】页面迁移 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

頁面遷移其實是伙伴管理算法中的一部分,鑒于其特殊性,特地另行分析。它是2007年的時候,2.6.24內(nèi)核版本開發(fā)時,新增碎片減少策略(the fragmentation reduction strategy)所引入的。該策略也稱之為反碎片技術(shù)(anti-gragmentation)。

根據(jù)《深入linux內(nèi)核架構(gòu)》的描述,反碎片的由來是因為Linux內(nèi)存管理長期存在一個問題:系統(tǒng)啟動并長期運行后,物理內(nèi)存將會產(chǎn)生很多碎片。暫且假設內(nèi)存頁面數(shù)為60,則長期運行后,其頁面的使用情況可能將會如下圖(灰色為已分配)。

雖然其未被分配的頁面仍有25%,但能夠申請到的最大頁面僅為一頁。不過這對用戶空間是沒有影響的,主要是由于用戶態(tài)的內(nèi)存是通過頁面映射而得到的。所以不在乎具體的物理頁面分布,其仍是可以將其映射為連續(xù)的一塊內(nèi)存提供給用戶態(tài)程序使用。于是用戶態(tài)可以感知的內(nèi)存則如下。

但是對于內(nèi)核態(tài),碎片則是個嚴肅的問題,因為大部分物理內(nèi)存都直接映射到內(nèi)核的永久映射區(qū)里面。如果真的存在碎片,將真的如第一張圖所示,無法映射到比一頁更大的內(nèi)存,這長期是linux的短板之一。于是為了解決該問題,則引入了反碎片。

linux內(nèi)核在內(nèi)存管理時,將已分配的頁面劃分為三種類型:

不可移動頁

——該類型頁面在內(nèi)存中位置固定,不可移動。內(nèi)核核心大部分內(nèi)存屬于該類型;

可回收頁

——不能夠直接移動,但是可以刪除,而內(nèi)容則可以從某些源重新生成。如文件數(shù)據(jù)映射的頁面則歸屬此類;

可移動頁

——可以隨意移動,分配給用戶態(tài)程序運行的用戶空間頁面則為該類。由于是通過頁面映射而得,將其復制到新位置后,更新映射表項,重新映射,應用程序是不感知的。

頁面的可遷移性則取決于它屬于哪一類。而內(nèi)核使用的反碎片技術(shù)則是基于將具有相同可移動性的頁分組的思想來實現(xiàn)的。當出現(xiàn)碎片的情況時,可移動頁面將會遷移,將為申請者騰出所需的連續(xù)頁面空間,由此避免了空閑頁面空間過于零碎而無法申請到大塊連續(xù)內(nèi)存。也由此,不可移動頁面不允許在可移動頁面中申請,避免因不可遷移而導致碎片。

其中具體遷移類型在頭文件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

——在內(nèi)存當中有固定的位置,不能移動。內(nèi)核的核心分配的內(nèi)存大多屬于這種類型;

MIGRATE_RECLAIMABLE

——不能直接移動,但可以刪除,其內(nèi)容頁可以從其他地方重新生成,例如,映射自文件的數(shù)據(jù)屬于這種類型,針對這種頁,內(nèi)核有專門的頁面回收處理;

MIGRATE_MOVABLE

——可以隨意移動,用戶空間應用程序所用到的頁屬于該類別。它們通過頁表來映射,如果他們復制到新的位置,頁表項也會相應的更新,應用程序不會注意到任何改變;

MIGRATE_PCPTYPES

——是per_cpu_pageset,即用來表示每CPU頁框高速緩存的數(shù)據(jù)結(jié)構(gòu)中的鏈表的遷移類型數(shù)目;

MIGRATE_RESERVE

——保留頁,是在前三種的列表中都沒用可滿足分配的內(nèi)存塊時,就可以從MIGRATE_RESERVE分配;

MIGRATE_CMA

——連續(xù)內(nèi)存分配,用于避免預留大塊內(nèi)存導致系統(tǒng)可用內(nèi)存減少而實現(xiàn)的,即當驅(qū)動不使用內(nèi)存時,將其分配給用戶使用,而需要時則通過回收或者遷移的方式將內(nèi)存騰出來。

MIGRATE_ISOLATE

——用于跨越NUMA節(jié)點移動物理內(nèi)存頁,該索引的頁不能分配,在大型系統(tǒng)上,它有益于將物理內(nèi)存頁移動到接近于是用該頁最頻繁地CPU;

MIGRATE_TYPES

——表示遷移類型的數(shù)目。

至于遷移類型的頁面管理實際上采用的還是伙伴管理算法的管理方式,內(nèi)存管理區(qū)zone的結(jié)構(gòu)里面的free_area是用于管理各階內(nèi)存頁面,而其里面的free_list則是對各遷移類型進行區(qū)分的鏈表。回顧內(nèi)存頁面釋放的函數(shù)__free_pages,其將空閑頁面掛回去的時候,是做了遷移類型區(qū)分的。也就是意味著頁面遷移類型是伴隨著伙伴管理算法的內(nèi)存管理構(gòu)建,根據(jù)遷移類型進行分而治之初始化。

那么各種遷移類型的頁面分配是如何運轉(zhuǎn)的?

頁面分配函數(shù)入口__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);

}

其首入?yún)⒃赺_alloc_pages_nodemask()里面會經(jīng)過allocflags_to_migratetype(gfp_mask)轉(zhuǎn)換獲取到申請頁面的類型。該遷移類型會在其內(nèi)部調(diào)用函數(shù)__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()僅是在指定遷移類型下自底向上進行各階遍歷查找所需的空閑頁面,而據(jù)上代碼其如果在指定遷移類型下分配失敗,且類型不為MIGRATE_RESERVE時,將會調(diào)用__rmqueue_fallback()進行分配。

接下來看一下__rmqueue_fallback()實現(xiàn):

【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;

}

可以看到其異于通常的伙伴管理算法,內(nèi)存頁面是由最高階開始進行查找的,而查找的遷移類型是根據(jù)fallbacks備選類型中進行遍歷獲得并止于MIGRATE_RESERVE類型。由此獲得的階號和遷移類型查找zone->free_area[]->free_list[]空閑頁面管理鏈表,如果查找到的話,則將其摘除,否則進入下一類型查找,最后所有類型都查找不到的時候,才會降階查找。

其中fallbacks[][]是已確定的類型順序結(jié)構(gòu),其定義為:

【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()函數(shù)實現(xiàn):

【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;

}

該函數(shù)主要實現(xiàn)了內(nèi)存頁面的遷移類型的變更,將__rmqueue_fallback()查找到滿足需要的內(nèi)存頁面空間類型轉(zhuǎn)為申請的類型。其中MIGRATE_CMA類型不做類型轉(zhuǎn)換,此外類型轉(zhuǎn)換的頁面數(shù)量為pageblock_nr_pages為單位的倍數(shù),還有就是對于階較低的內(nèi)存頁面(小于pageblock_order/2)、類型不為MIGRATE_RECLAIMABLE且未開啟頁面遷移的情況下,是不做類型轉(zhuǎn)換的。完了,在__rmqueue_fallback()里面根據(jù)其轉(zhuǎn)換后的類型通過expand()擴展到對應的遷移類型伙伴管理系統(tǒng)中。

小結(jié)一下,__rmqueue_fallback()是自高往低階遍歷fallbacks遷移類型表,查找滿足分配需要的內(nèi)存頁面,然后將查找到的內(nèi)存頁面進行類型變更后合并到所申請的類型中,以實現(xiàn)類型遷移。值得注意的是,之所以內(nèi)存遷移都是以內(nèi)存塊的高階進行的,主要就是為了反碎片化,避免當前類型無法滿足需要的時候,過于頻繁地向備選類型進行小片內(nèi)存申請和做遷移而導致備選類型的內(nèi)存頁面產(chǎn)生大量水平,將問題控制在所申請的內(nèi)存類型中。

最后看一下set_pageblock_migratetype()的實現(xiàn):

【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;

}

}

其中g(shù)et_pageblock_bitmap()用于取得zone結(jié)構(gòu)體中pageblock_flags成員,而后面則是基于此做位圖操作。通過該函數(shù),可以看到內(nèi)存頁面的類型管理是通過其所屬的zone的結(jié)構(gòu)體中的pageblock_flags所管理的位圖進行標識該頁面的遷移類型。

總結(jié)

以上是生活随笔為你收集整理的linux内核源码剖析 博客,【Linux内存源码分析】页面迁移的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。