溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

linux內(nèi)存回收機(jī)制

發(fā)布時(shí)間:2020-08-10 23:24:28 來(lái)源:ITPUB博客 閱讀:209 作者:531968912 欄目:建站服務(wù)器

1 回收哪些頁(yè)面

Page cache;

用戶地址空間的內(nèi)存映射頁(yè)面;

Slab緩存:如dentryinode cache;

匿名頁(yè):進(jìn)程堆棧和mmap匿名映射內(nèi)存區(qū);回收前先換置到swap

 

 

2 何時(shí)回收

Kswapd定期喚醒:當(dāng)系統(tǒng)空閑內(nèi)存小于閾值則進(jìn)行頁(yè)面回收;

直接頁(yè)面回收:假設(shè)操作系統(tǒng)需要通過(guò)伙伴系統(tǒng)為用戶進(jìn)程分配一大塊內(nèi)存,或者需要?jiǎng)?chuàng)建一個(gè)很大的緩沖區(qū),而當(dāng)時(shí)系統(tǒng)中的內(nèi)存沒(méi)有辦法提供足夠多的物理內(nèi)存以滿足這種內(nèi)存請(qǐng)求,這時(shí)候,操作系統(tǒng)就必須盡快進(jìn)行頁(yè)面回收操作;

OS嘗試內(nèi)存回收后仍無(wú)法獲取足夠頁(yè)面,則調(diào)用find_bad_process并進(jìn)行OOM kill;

 

 

3 如何回收

基于LRU算法;每個(gè)zone維護(hù)兩個(gè)LRU鏈表

struct zone {

    ……

          spinlock_t                   lru_lock;          

          struct list_head   active_list;

          struct list_head     inactive_list;

          unsigned long                  nr_active;

          unsigned long                  nr_inactive;

    ……

        

 }

PG_active/PG_referenced用于標(biāo)識(shí)頁(yè)面活躍度,前者標(biāo)識(shí)頁(yè)面時(shí)活躍的;后者表示頁(yè)面最近是否被訪問(wèn)過(guò),每訪問(wèn)一次便會(huì)置位;

注:假如只是用一個(gè)標(biāo)志符,在頁(yè)面被訪問(wèn)時(shí),置位該標(biāo)志符,之后該頁(yè)面一直處于活躍狀態(tài),如果操作系統(tǒng)不清除該標(biāo)志位,那么即使之后很長(zhǎng)一段時(shí)間內(nèi)該頁(yè)面都沒(méi)有或很少被訪問(wèn)過(guò),該頁(yè)面也還是處于活躍狀態(tài)。為了能夠有效清除該標(biāo)志位,需要有定時(shí)器的支持以便于在超時(shí)時(shí)間之后該標(biāo)志位可以自動(dòng)被清除。然而,很多 Linux 支持的體系結(jié)構(gòu)并不能提供這樣的硬件支持,所以 Linux 中使用兩個(gè)標(biāo)志符來(lái)判斷頁(yè)面的活躍程度。

 

Linux依據(jù)這兩個(gè)字段將pageactive_listinactive_list之間移動(dòng);
linux內(nèi)存回收機(jī)制

注:1 表示函數(shù) mark_page_accessed()2 表示函數(shù) page_referenced(),3 表示函數(shù) activate_page()4 表示函數(shù) shrink_active_list()
linux內(nèi)存回收機(jī)制


 

不管是kswapd還是直接頁(yè)面回收,最終都調(diào)用shrink_slabshrink_zone

直接頁(yè)面回收:反復(fù)調(diào)用這兩個(gè)函數(shù),若特定循環(huán)次數(shù)內(nèi)沒(méi)能成功釋放N個(gè)page,則調(diào)用OOM killer;

Kswapd:對(duì)每個(gè)zone都調(diào)用shrink_zone()

 

3.1 Shrink_slab原理

先向操作系統(tǒng)內(nèi)核注冊(cè) shrinker函數(shù),會(huì)在內(nèi)存較少的時(shí)候主動(dòng)釋放一些該磁盤緩存占用的空間。

函數(shù) shrink_slab() 會(huì)遍歷 shrinker 鏈表,從而對(duì)所有注冊(cè)了 shrinker 函數(shù)的磁盤緩存進(jìn)行處理。

注冊(cè) shrinker 是通過(guò)函數(shù) set_shrinker() 實(shí)現(xiàn)的,解除 shrinker 注冊(cè)是通過(guò)函數(shù) remove_shrinker() 實(shí)現(xiàn)的。當(dāng)前,Linux 操作系統(tǒng)中主要的 shrinker 函數(shù)有如下幾種:

shrink_dcache_memory():該 shrinker 函數(shù)負(fù)責(zé) dentry 緩存。

shrink_icache_memory():該 shrinker 函數(shù)負(fù)責(zé) inode 緩存。

mb_cache_shrink_fn():該 shrinker 函數(shù)負(fù)責(zé)用于文件系統(tǒng)元數(shù)據(jù)的緩存。

 

3.2 Shrink_zone原理

1 通過(guò)shrink_active_list()將頁(yè)面從active移到inactive list;

2 調(diào)用shrink_inactive_list()inactive list的頁(yè)放入臨時(shí)鏈表,最終調(diào)用shrink_page_list()回收
linux內(nèi)存回收機(jī)制


 

3.2.1 Swappiness的意義

上文提到的shrink_zone() 會(huì)調(diào)用 shrink_lruvec(),而active/inactive list又各分為anon匿名頁(yè)和file cache映射頁(yè)鏈表,總計(jì)4個(gè)LRU;

swappines只針對(duì)anon page,即便其為0也有可能執(zhí)行swap。

vmscan.c中的get_scan_coun()

1. 首先如果系統(tǒng)禁用了swap或者沒(méi)有swap空間,則只掃描file based的鏈表,即不進(jìn)行匿名頁(yè)鏈表掃描

 代碼:

       if (!sc->may_swap || (get_nr_swap_pages() <= 0)) {

                scan_balance = SCAN_FILE;

                goto out;

        }

 

2. 如果當(dāng)前進(jìn)行的不是全局頁(yè)回收(cgroup資源限額引起的頁(yè)回收),并且swappiness設(shè)為0,則不進(jìn)行匿名頁(yè)鏈表掃描,

代碼:

        if (!global_reclaim(sc) && !vmscan_swappiness(sc)) {

                scan_balance = SCAN_FILE;

                goto out;

        }

 

3. 如果進(jìn)行鏈表掃描前設(shè)置的priority(這個(gè)值決定掃描多少分之一的鏈表元素)0,且swappiness0,則可能會(huì)進(jìn)行swap

代碼:

        if (!sc->priority && vmscan_swappiness(sc)) {

                scan_balance = SCAN_EQUAL;

                goto out;

        }

 

4. 如果是全局頁(yè)回收,并且當(dāng)前空閑內(nèi)存和所有file based鏈表page數(shù)目的加和都小于系統(tǒng)的high watermark,則必須進(jìn)行匿名頁(yè)回收,則必然會(huì)發(fā)生swap

代碼:

        anon  = get_lru_size(lruvec, LRU_ACTIVE_ANON) +

                get_lru_size(lruvec, LRU_INACTIVE_ANON);

        file  = get_lru_size(lruvec, LRU_ACTIVE_FILE) +

                get_lru_size(lruvec, LRU_INACTIVE_FILE);

 

        if (global_reclaim(sc)) {

                free = zone_page_state(zone, NR_FREE_PAGES);

                if (unlikely(file + free <= high_wmark_pages(zone))) {

                        scan_balance = SCAN_ANON;

                        goto out;

                }

        }

 

5. 如果系統(tǒng)inactive file鏈表比較充足,則不考慮進(jìn)行匿名頁(yè)的回收,即不進(jìn)行swap

代碼:

        if (!inactive_file_is_low(lruvec)) {

                scan_balance = SCAN_FILE;

                goto out;

        }

注:每個(gè)zonemin/low/high 3個(gè)值,而high watermark指的是最后一個(gè),這3個(gè)值依據(jù)vm.min_free_kbytes設(shè)置

 

 

3.2.2 反向映射

回收物理頁(yè)前需要解除所有關(guān)聯(lián)該頁(yè)的頁(yè)表項(xiàng),而共享內(nèi)存中的頁(yè)可能被多個(gè)進(jìn)程引用,因此需要一種機(jī)制快速定位頁(yè)表項(xiàng);

2.4要遍歷所有進(jìn)程;

2.5引入反向映射,每個(gè)物理頁(yè)維護(hù)一個(gè)頁(yè)表項(xiàng)鏈表;

2.6引入基于對(duì)象的反向映射,每個(gè)物理頁(yè)設(shè)置一個(gè)反向映射鏈表,鏈表節(jié)點(diǎn)為vm_area_struct結(jié)構(gòu),其通過(guò)mm_struct找到pgd進(jìn)而找到相應(yīng)頁(yè)表項(xiàng);

struct page {

          atomic_t _mapcount; --初始值是 -1,每增加一個(gè)使用者,該計(jì)數(shù)器加 1

          union {

        ……

             struct {

                   ……           

                    struct address_space *mapping; --如果最低位置位,為指向 anon_vma 結(jié)構(gòu)(用于匿名頁(yè)面)的指針;否則為 address_space 指針(用于基于文件映射的頁(yè)面)。

             };

        ……

 };

對(duì)于匿名頁(yè)面來(lái)說(shuō),頁(yè)面雖然可以是共享的,但是一般情況下,共享匿名頁(yè)面的使用者的數(shù)目不會(huì)很多;而對(duì)于基于文件映射的頁(yè)面來(lái)說(shuō),共享頁(yè)面的使用者的數(shù)目可能會(huì)非常多,使用優(yōu)先級(jí)搜索樹這種結(jié)構(gòu)可以更加快速地定位那些引用了該頁(yè)面的虛擬內(nèi)存區(qū)域。操作系統(tǒng)會(huì)為每一個(gè)文件都建立一個(gè)優(yōu)先級(jí)搜索樹,其根節(jié)點(diǎn)可以通過(guò)結(jié)構(gòu) address_space 中的 i_mmap 字段獲取。

linux內(nèi)存回收機(jī)制

 

 

 

注:LRU緩存

頁(yè)面根據(jù)其活躍程度會(huì)在 active 鏈表和 inactive 鏈表之間來(lái)回移動(dòng),如果要將某個(gè)頁(yè)面插入到這兩個(gè)鏈表中去,必須要通過(guò)自旋鎖以保證對(duì)鏈表的并發(fā)訪問(wèn)操作不會(huì)出錯(cuò)。為了降低鎖的競(jìng)爭(zhēng),Linux 提供了一種特殊的緩存:LRU 緩存,用以批量地向 LRU 鏈表中快速地添加頁(yè)面。有了 LRU 緩存之后,新頁(yè)不會(huì)被馬上添加到相應(yīng)的鏈表上去,而是先被放到一個(gè)緩沖區(qū)中去,當(dāng)該緩沖區(qū)緩存了足夠多的頁(yè)面之后,緩沖區(qū)中的頁(yè)面才會(huì)被一次性地全部添加到相應(yīng)的 LRU 鏈表中去。

LRU 緩存用到了 pagevec 結(jié)構(gòu),如下所示 :

 struct pagevec {

          unsigned long nr;

          unsigned long cold;

          struct page *pages[PAGEVEC_SIZE];

 };

lru_cache_add() lru_cache_add_active()。前者用于延遲將頁(yè)面添加到 inactive 鏈表上去,后者用于延遲將頁(yè)面添加到 active 鏈表上去。這兩個(gè)函數(shù)都會(huì)將要移動(dòng)的頁(yè)面先放到頁(yè)向量 pagevec 中,當(dāng) pagevec 滿了(已經(jīng)裝了 14 個(gè)頁(yè)面的描述符指針),pagevec 結(jié)構(gòu)中的所有頁(yè)面才會(huì)被一次性地移動(dòng)到相應(yīng)的鏈表上去。

 

 

 

參考資料

http://www.ibm.com/developerworks/cn/linux/l-cn-pagerecycle/index.html?ca=dat

http://www.douban.com/note/349467816/

 

 

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI