自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Linux內(nèi)存管理--內(nèi)存回收

系統(tǒng) Linux
在內(nèi)存回收過(guò)程中,有哪些內(nèi)存可以回收,什么時(shí)候進(jìn)行回收,回收內(nèi)存時(shí)如何盡可能的減少對(duì)系統(tǒng)性能的影響,回收內(nèi)存的策略,這些是我們著重要關(guān)注的問(wèn)題,也是本文主要闡述的重點(diǎn)。

內(nèi)存的回收在Linux內(nèi)存管理中占據(jù)非常重要的地位,系統(tǒng)的內(nèi)存畢竟是有限的,跑的進(jìn)程成百上千,系統(tǒng)內(nèi)存越來(lái)越小,我們必須選擇一些內(nèi)存進(jìn)行回收,以滿足別的任務(wù)的需求。在內(nèi)存回收過(guò)程中,有哪些內(nèi)存可以回收,什么時(shí)候進(jìn)行回收,回收內(nèi)存時(shí)如何盡可能的減少對(duì)系統(tǒng)性能的影響,回收內(nèi)存的策略,這些是我們著重要關(guān)注的問(wèn)題,也是本文主要闡述的重點(diǎn)。

1.1 內(nèi)存回收的目標(biāo)


不是所有的物理內(nèi)存都可以參與回收的,比如要是把內(nèi)核代碼段的內(nèi)存給回收了,系統(tǒng)就無(wú)法正常運(yùn)行了,一般內(nèi)核代碼段,數(shù)據(jù)段,內(nèi)核kmalloc()出來(lái)的內(nèi)存,內(nèi)核線程占用的內(nèi)存等都是不可以回收的,除此之外的內(nèi)存都是我們要回收的目標(biāo)。

回收的內(nèi)存主要是由用戶態(tài)進(jìn)程占用的內(nèi)存和內(nèi)核自己在運(yùn)行時(shí)所使用的一些內(nèi)存組成。用戶態(tài)進(jìn)程占用的內(nèi)存主要是我們常見(jiàn)的進(jìn)程代碼段,數(shù)據(jù)段,堆棧等,內(nèi)核運(yùn)行使用的內(nèi)存主要是磁盤(pán)高速緩存(如索引節(jié)點(diǎn),目錄項(xiàng)高速緩存),頁(yè)面高速緩存(訪問(wèn)文件時(shí)系統(tǒng)生成的頁(yè)面cache),mmap()文件時(shí)所用的有名映射所使用的物理內(nèi)存。后面的這些內(nèi)才能雖然也是內(nèi)核管理使用的內(nèi)存,但對(duì)其進(jìn)行回收的時(shí)候,頂多影響內(nèi)核的性能,而不會(huì)導(dǎo)致系統(tǒng)無(wú)法運(yùn)行。

1.2 內(nèi)存回收的時(shí)機(jī)


1、內(nèi)存緊缺回收:grow_buffers()無(wú)法獲取緩沖區(qū)頁(yè),alloc_page_buffers()無(wú)法獲取頁(yè)臨時(shí)緩沖區(qū)首部,__alloc_pages()無(wú)法再給定的內(nèi)存區(qū)分配一組連續(xù)頁(yè)框。

2、周期回收:必要時(shí),激活相應(yīng)內(nèi)核線程執(zhí)行內(nèi)存回收算法:kswapd()內(nèi)核線程,檢查某個(gè)內(nèi)存管理區(qū)的空閑頁(yè)框數(shù)是否已低于pages_high值的標(biāo)高。events內(nèi)核線程,一個(gè)工作者線程,回收位于高速內(nèi)存緩存中的所有空閑的slab。

1.3 內(nèi)存回收的策略


1.3.1 內(nèi)存回收的分類(lèi)

內(nèi)存回收主要是要回收兩類(lèi)內(nèi)存:最近最少使用的內(nèi)存以及高速內(nèi)存緩存中空閑的slab。前者主要包括用戶態(tài)進(jìn)程的代碼段,數(shù)據(jù)段,堆棧,文件映射內(nèi)存,頁(yè)高速內(nèi)存,后者主要包括磁盤(pán)高速緩存及一些其他的空閑內(nèi)存高速緩存。

最近最少使用內(nèi)存存放在一個(gè)lru鏈表上,每個(gè)內(nèi)存管理區(qū)zone都有一個(gè)lru結(jié)構(gòu),里面含有active和inactive兩個(gè)鏈表頭,active鏈表上記錄當(dāng)前的活躍的報(bào)文,inactive用來(lái)記錄當(dāng)前不活躍的報(bào)文。一般我們回首lru上的inactive鏈表上的內(nèi)存頁(yè)。同時(shí),在內(nèi)存回收的過(guò)程中,會(huì)從active鏈表向inactive鏈表上補(bǔ)充對(duì)應(yīng)的最近最少使用內(nèi)存頁(yè)。每個(gè)內(nèi)存頁(yè)的內(nèi)核數(shù)據(jù)結(jié)構(gòu)page上有一個(gè)標(biāo)記位PG_referenced,該標(biāo)記位使得一個(gè)頁(yè)從"不活動(dòng)“狀態(tài)轉(zhuǎn)為”活動(dòng)“狀態(tài)的時(shí)間加倍,反之亦然。比如:一個(gè)頁(yè)面可能1個(gè)小時(shí)內(nèi)沒(méi)人反應(yīng),不能因?yàn)榕既坏囊淮卧L問(wèn)就認(rèn)為它是活躍的,得兩次才認(rèn)為它是一個(gè)活躍的頁(yè)面。下面是頁(yè)面在inactive和active鏈表上轉(zhuǎn)移的變化圖。

Slab內(nèi)存高速緩存中經(jīng)常會(huì)有一些完全空閑的slab,這些是我們回收的另一個(gè)目標(biāo)。

1.3.2 反向映射

對(duì)于可以通過(guò)用戶態(tài)線性地址空間可以直接訪問(wèn)到的物理頁(yè)來(lái)說(shuō),可以分為匿名頁(yè)和文件映射頁(yè)兩類(lèi),匿名頁(yè)指的是不與具體文件對(duì)應(yīng)映射的物理頁(yè),比如代碼段,堆棧等使用的物理頁(yè),映射頁(yè)指的是映射到文件某一部分的物理頁(yè),通常使用mmap()來(lái)進(jìn)行相關(guān)的映射。

對(duì)于匿名映射和文件映射來(lái)說(shuō),可能一段物理內(nèi)存會(huì)在多個(gè)進(jìn)程的頁(yè)表中使用,比如對(duì)于匿名映射,fork()一個(gè)進(jìn)程,一開(kāi)始會(huì)共用父進(jìn)程的物理內(nèi)存,對(duì)于文件映射,多個(gè)進(jìn)程可能同時(shí)映射到一個(gè)文件的同一部分文件。所以在頁(yè)面回收時(shí),需要將該頁(yè)面在所有的頁(yè)表引用中給去除掉。這種手段稱(chēng)為反向映射。想要找到使用這些物理頁(yè)的頁(yè)表項(xiàng)的話,需要先找到引用他們的頁(yè)表,而頁(yè)表的地址記錄在每個(gè)進(jìn)程的內(nèi)存描述符里面,同時(shí)用來(lái)描述進(jìn)程用戶態(tài)地址空間的每個(gè)vm_area_struct都記錄了一個(gè)指針,指向所屬的內(nèi)存描述符。因此只要通過(guò)物理頁(yè)找到引用他們的vm_area_struct,就能找到內(nèi)存描述符,從而找到頁(yè)表,找到對(duì)應(yīng)的頁(yè)表項(xiàng)。

匿名頁(yè)的反向映射:

對(duì)于匿名頁(yè)來(lái)說(shuō),每個(gè)頁(yè)面的mapping字段指向一個(gè)anon_vma描述符,anon_vma描述符中存在一個(gè)鏈表頭,所有引用該頁(yè)面的vm_area_struct都存放在里面。page,anon_vma,vm_area_struct這些數(shù)據(jù)結(jié)構(gòu)的關(guān)系如下圖所示:

對(duì)于匿名頁(yè)來(lái)說(shuō),其被別的地址空間引用,基本上都是因?yàn)閒ork()進(jìn)程時(shí),子進(jìn)程復(fù)制父進(jìn)程的地址空間,從而被引用的。各個(gè)vm_area_struct加入anon_vma的鏈表的過(guò)程如下:

假設(shè)當(dāng)前一個(gè)進(jìn)程p,后來(lái)fork出一個(gè)子進(jìn)程c。

1、當(dāng)進(jìn)程P為某個(gè)vm_area_struct加入***個(gè)物理頁(yè)時(shí),比如說(shuō)發(fā)生了缺頁(yè)異常,動(dòng)態(tài)分配一個(gè)anon_vma的數(shù)據(jù)結(jié)構(gòu),將vm_area_struct加入該anon_vma所管理的鏈表,vm_area_struct結(jié)構(gòu)中的anon_vma字段指向該anon_vma,同時(shí)把該頁(yè)面中的mapping字段賦值為該anon_vma.對(duì)于后續(xù)為該vm_area_struct申請(qǐng)的物理頁(yè)面,其mapping字段都賦值為該anon_vma。

2、當(dāng)該進(jìn)程p執(zhí)行fork()時(shí),在fork的處理過(guò)程中,會(huì)調(diào)用dup_mmap()來(lái)復(fù)制進(jìn)程p的線性地址空間,在dup_mmap()會(huì)復(fù)制進(jìn)程p的每一個(gè)vm_area_struct,加入到自己的地址空間中,并將vm_area_struct加入到anon_vma所管理的鏈表中,參看anon_vma_link()。此時(shí)為進(jìn)程p申請(qǐng)的頁(yè)面被進(jìn)程c共享,通過(guò)頁(yè)面的mapping字段可以找到anon_vma,從anon_vma可以遍歷進(jìn)程p,c。

3、考慮一個(gè)問(wèn)題,在進(jìn)程c中才觸發(fā)缺頁(yè)異常被申請(qǐng)的內(nèi)存頁(yè),其mmapping被賦值為所屬vm_area_struct的vma_anon,但進(jìn)程p并沒(méi)有使用到該頁(yè),所以一個(gè)物理頁(yè)mapping字段指向的vma_anon所下掛的vm_area_struct可能并不包含該物理頁(yè)。

文件映射頁(yè)的反向映射:

對(duì)于每個(gè)文件映射頁(yè),其page mapping字段指向的是對(duì)應(yīng)文件的address_space數(shù)據(jù)結(jié)構(gòu),address_space中有個(gè) struct prio_tree_root i_mmap 字段,指向一個(gè)優(yōu)先樹(shù),優(yōu)先樹(shù)里面會(huì)把所有映射該文件內(nèi)容的vm_area_struct 給組織起來(lái)。在該樹(shù)中,其樹(shù)的節(jié)點(diǎn)基地址和堆地址分別是映射的文件內(nèi)容的起始地址和結(jié)束地址,要是多個(gè)進(jìn)程同時(shí)映射該地址段,會(huì)用鏈表在該節(jié)點(diǎn)上將vm_area_struct串起來(lái)。

1.3.3 內(nèi)存回收流程介紹     

睡眠回收我們不關(guān)注,主要介紹內(nèi)存緊缺回收及周期回收:

1、內(nèi)存緊缺回收主要函數(shù)是try_to_free_pages(),該函數(shù)會(huì)執(zhí)行一個(gè)循環(huán),按照優(yōu)先級(jí)從12到0,依次調(diào)用shrink_caches(),shrink_slab()來(lái)回收頁(yè)面,直到回收至少32個(gè)內(nèi)存頁(yè)面。

依次調(diào)用以下輔助函數(shù):

shrink_caches():調(diào)用shrink_zone()對(duì)傳入的zone鏈表中的每個(gè)zone,進(jìn)行l(wèi)ru上面的頁(yè)面回收。

shrink_slab():對(duì)磁盤(pán)索引節(jié)點(diǎn)cache和目錄項(xiàng)索引節(jié)點(diǎn)等磁盤(pán)高速緩存進(jìn)行回收,由于磁盤(pán)索引節(jié)點(diǎn)和目錄項(xiàng)索引節(jié)點(diǎn)都是從slab高速緩存中分配的,這樣就會(huì)導(dǎo)致空閑slab的產(chǎn)生,空閑slab后續(xù)會(huì)在周期性回收的cache_reap工作隊(duì)列中被回收。估計(jì)也就是因?yàn)樽罱K會(huì)清零空閑slab,才會(huì)起這么一個(gè)函數(shù)名。^_^

shrink_zone():對(duì)內(nèi)存管理區(qū)上的lru鏈表中的非活躍頁(yè)面進(jìn)行回收,在非活躍頁(yè)面不足的時(shí)候,調(diào)用refill_inactive_zone()對(duì)lru上的inactive鏈表補(bǔ)充非活躍頁(yè)面,同時(shí)shrink_zone()調(diào)用shrink_cache()來(lái)進(jìn)行頁(yè)面的回收,該函數(shù)的具體解析可以參照下面的源碼淺析。

shrink_list():該輔助函數(shù)在shrink_cache()中被調(diào)用,該函數(shù)對(duì)在shrink_cache()中傳入的非活躍page列表進(jìn)行遍歷,對(duì)每個(gè)頁(yè)面進(jìn)行回收工作,該函數(shù)的具體解析可以參考下面的源碼解析。

refill_inactie_zone():該輔助函數(shù)根據(jù)一定的規(guī)則將處于lru active鏈表上的活躍頁(yè)面移動(dòng)到inactive鏈表上,以補(bǔ)充可以回收的頁(yè)面,在lru鏈表里有兩類(lèi)頁(yè),一類(lèi)是屬于用戶態(tài)空間的頁(yè),比如用戶態(tài)進(jìn)程的代碼段,數(shù)據(jù)段,一類(lèi)是在頁(yè)高速緩存中的頁(yè),系統(tǒng)為了降低對(duì)應(yīng)用程序的影響,將要優(yōu)先將頁(yè)高速緩存頁(yè)進(jìn)行回收,同時(shí)為了系統(tǒng)整體性能也會(huì)適當(dāng)回收用戶態(tài)進(jìn)程頁(yè)。按照如下經(jīng)驗(yàn)公式進(jìn)行選擇:

交換傾向值=映射比率/2+負(fù)荷值+交換值

2、kswapd進(jìn)程一般會(huì)在系統(tǒng)中睡眠,但當(dāng)__alloc_page()發(fā)現(xiàn)各個(gè)管理區(qū)的剩余頁(yè)面都低于警告值(由內(nèi)存管理描述符的pages_low字段和protection字段推算出來(lái))時(shí),會(huì)激活kswapd進(jìn)程進(jìn)行頁(yè)面回收,直到回收的頁(yè)面使得管理區(qū)的剩余頁(yè)面高于zone->pages_high時(shí)才停止回收,本質(zhì)上也是調(diào)用了shrink_zone()和shrink_slab()。

3、cache_reap工作隊(duì)列定期運(yùn)行來(lái)回收slab高速緩存中空閑的slab占用的頁(yè)。

#p#

1.4 相關(guān)源代碼的淺析


 

  1. static void 
  2. shrink_zone(struct zone *zone, struct scan_control *sc) 
  3. unsigned long nr_active; 
  4. unsigned long nr_inactive; 
  5.   
  6.   
  7. //根據(jù)優(yōu)先級(jí),得到可以掃描的頁(yè)面數(shù),優(yōu)先級(jí)越高, 
  8. //代表越不急迫,可以掃描的頁(yè)面數(shù)也最少 
  9. zone->nr_scan_active += (zone->nr_active >> sc->priority) + 1; 
  10. nr_active = zone->nr_scan_active; 
  11. if (nr_active >= SWAP_CLUSTER_MAX) 
  12. zone->nr_scan_active = 0
  13. else 
  14. nr_active = 0
  15.   
  16.   
  17. zone->nr_scan_inactive += (zone->nr_inactive >> sc->priority) + 1; 
  18. nr_inactive = zone->nr_scan_inactive; 
  19. //非活動(dòng)頁(yè)比較少的話,可以先忽略過(guò)去,將跳過(guò)的頁(yè)面記錄到nr_scan_inactive中 
  20. //留待下一次再處理 
  21. if (nr_inactive >= SWAP_CLUSTER_MAX) 
  22. zone->nr_scan_inactive = 0
  23. else 
  24. nr_inactive = 0
  25.   
  26.  
  27. //設(shè)置需要回收的頁(yè)面數(shù)為32個(gè) 
  28. sc->nr_to_reclaim = SWAP_CLUSTER_MAX
  29.   
  30.   
  31. //開(kāi)始回收頁(yè)面,每次掃描32個(gè)頁(yè)面,多了不干噢!!! 
  32.   
  33. while (nr_active || nr_inactive) { 
  34. if (nr_active) { 
  35. //設(shè)置每次要掃描的非活動(dòng)頁(yè)面數(shù),需要將其放 
  36. //入到inactive list里面 
  37. sc->nr_to_scan = min(nr_active, 
  38. (unsigned long)SWAP_CLUSTER_MAX); 
  39. nr_active -sc->nr_to_scan; 
  40. //補(bǔ)充inactive list中的頁(yè)面 
  41. refill_inactive_zone(zone, sc); 
  42.   
  43.   
  44. if (nr_inactive) { 
  45. //設(shè)置每次將要掃描的頁(yè)面,最多也就32個(gè)頁(yè)面 
  46. sc->nr_to_scan = min(nr_inactive, 
  47. (unsigned long)SWAP_CLUSTER_MAX); 
  48.   
  49. nr_inactive -sc->nr_to_scan; 
  50. //開(kāi)始正式回收inactive list中的頁(yè)面 
  51. shrink_cache(zone, sc); 
  52. //32個(gè)頁(yè)面被回收完畢,大功告成了!!! 
  53. if (sc->nr_to_reclaim <= 0) 
  54. break; 
  55.   
  56.  
  57. static int shrink_list(struct list_head *page_list, struct scan_control *sc) 
  58. LIST_HEAD(ret_pages); 
  59. struct pagevec freed_pvec; 
  60. int pgactivate = 0
  61. int reclaimed = 0
  62. //有進(jìn)程需要調(diào)度,先進(jìn)行調(diào)度 
  63. cond_resched(); 
  64.   
  65.   
  66. pagevec_init(&freed_pvec, 1); 
  67. //對(duì)于page_list 鏈表上的每一個(gè)頁(yè)面試圖進(jìn)行回收 
  68. while (!list_empty(page_list)) { 
  69. struct address_space *mapping; 
  70. struct page *page; 
  71. int may_enter_fs; 
  72. int referenced; 
  73.   
  74.   
  75. //獲取一個(gè)頁(yè)面 
  76. page = lru_to_page(page_list); 
  77. //從lru上摘除 
  78. list_del(&page->lru); 
  79. //page被鎖定,不能回收 
  80. if (TestSetPageLocked(page))//page is locked? 
  81. goto keep; 
  82.   
  83.   
  84. BUG_ON(PageActive(page)); 
  85. //page正在被writeback,不能回收 
  86. if (PageWriteback(page))//page is writeback? 
  87. goto keep_locked; 
  88.   
  89. sc->nr_scanned++; 
  90. /* Double the slab pressure for mapped and swapcache pages */ 
  91.   
  92.   
  93. if (page_mapped(page) || PageSwapCache(page)) 
  94. sc->nr_scanned++; 
  95. //查看最近該頁(yè)面有無(wú)被訪問(wèn)過(guò) 
  96. referenced = page_referenced(page, 1, sc->priority <= 0); 
  97. /* In active use or really unfreeable?  Activate it. */ 
  98. //1頁(yè)面被訪問(wèn)過(guò),2頁(yè)面在用戶態(tài)空間,頁(yè)面是文件映射頁(yè)面, 
  99. //頁(yè)面在交換高速緩存中,同時(shí)滿足這兩個(gè)條件的話,頁(yè)面不被回收 
  100. if (referenced && page_mapping_inuse(page)) 
  101. goto activate_locked; 
  102.   
  103.   
  104. #ifdef CONFIG_SWAP 
  105.   
  106. //page is anon and page has not been add to swapcache 
  107. //該頁(yè)面是匿名映射的頁(yè)面,且該頁(yè)面不在swapcache中 
  108. if (PageAnon(page) && !PageSwapCache(page)) { 
  109. //將頁(yè)面加入到swap cache中 
  110. if (!add_to_swap(page)) 
  111. goto activate_locked; 
  112. #endif /* CONFIG_SWAP */ 
  113. //得到對(duì)應(yīng)的address_space,有可能是對(duì)應(yīng)文件的address_space,或者是 
  114. //swap cache的address_space 
  115. mapping = page_mapping(page); 
  116. may_enter_fs = (sc->gfp_mask & __GFP_FS) || 
  117. (PageSwapCache(page) && (sc->gfp_mask & __GFP_IO)); 
  118.   
  119.   
  120. //該頁(yè)面被映射到某個(gè)用戶頁(yè)表中 
  121. if (page_mapped(page) && mapping) { 
  122. //將該頁(yè)面在用戶頁(yè)表中的頁(yè)表項(xiàng)通通清除 
  123. switch (try_to_unmap(page)) { 
  124. case SWAP_FAIL: 
  125. goto activate_locked; 
  126. case SWAP_AGAIN: 
  127. goto keep_locked; 
  128. case SWAP_SUCCESS: 
  129. ; /* try to free the page below */ 
  130. //頁(yè)面是臟的,哈哈,準(zhǔn)備往文件或swapcache里面寫(xiě)硬盤(pán)吧 
  131. if (PageDirty(page)) { 
  132. if (referenced) 
  133. goto keep_locked; 
  134. if (!may_enter_fs) 
  135. goto keep_locked; 
  136. if (laptop_mode && !sc->may_writepage) 
  137. goto keep_locked; 
  138.   
  139.   
  140. /* Page is dirty, try to write it out here */ 
  141. //往磁盤(pán)上寫(xiě)頁(yè)面 
  142. switch(pageout(page, mapping)) { 
  143. case PAGE_KEEP: 
  144. goto keep_locked; 
  145. case PAGE_ACTIVATE: 
  146. goto activate_locked; 
  147. case PAGE_SUCCESS: 
  148. if (PageWriteback(page) || PageDirty(page)) 
  149. goto keep; 
  150.   
  151. if (TestSetPageLocked(page)) 
  152. goto keep; 
  153. if (PageDirty(page) || PageWriteback(page)) 
  154. goto keep_locked; 
  155. mapping = page_mapping(page); 
  156. case PAGE_CLEAN: 
  157. ; /* try to free the page below */ 
  158.   
  159.   
  160.   
  161. //若頁(yè)面是緩沖區(qū)頁(yè)面,將對(duì)應(yīng)的buffer_head給釋放掉 
  162. if (PagePrivate(page)) { 
  163. if (!try_to_release_page(page, sc->gfp_mask)) 
  164. goto activate_locked; 
  165. if (!mapping && page_count(page) == 1) 
  166. goto free_it; 
  167.   
  168.   
  169. if (!mapping) 
  170. goto keep_locked; 
  171. /* truncate got there first */ 
  172.   
  173.   
  174. spin_lock_irq(&mapping->tree_lock); 
  175.   
  176.   
  177.   
  178. //頁(yè)面為臟頁(yè)面或者page的引用計(jì)數(shù)為2,都是不可以回收的  
  179. if (page_count(page) != 2 || PageDirty(page)) { 
  180. spin_unlock_irq(&mapping->tree_lock); 
  181. goto keep_locked; 
  182.   
  183.   
  184. #ifdef CONFIG_SWAP 
  185. //到達(dá)這里,說(shuō)明該page只被swap cache或者頁(yè)高速緩存及 
  186. //fpra所共有,需要將其從swap cache上或者頁(yè)高速緩存上刪除。 
  187. if (PageSwapCache(page)) { 
  188. swp_entry_t swap = { .val = page->private }; 
  189. //從swap cache上進(jìn)行刪除 
  190. __delete_from_swap_cache(page); 
  191. spin_unlock_irq(&mapping->tree_lock); 
  192. swap_free(swap); 
  193. __put_page(page); 
  194. /* The pagecache ref */ 
  195. goto free_it; 
  196. #endif /* CONFIG_SWAP */ 
  197.   
  198.   
  199. //從頁(yè)面高速緩存中將該頁(yè)面刪除 
  200. __remove_from_page_cache(page); 
  201. spin_unlock_irq(&mapping->tree_lock); 
  202. __put_page(page); 
  203.   
  204.   
  205. free_it: 
  206. unlock_page(page); 
  207. reclaimed++; 
  208. if (!pagevec_add(&freed_pvec, page)) 
  209. __pagevec_release_nonlru(&freed_pvec); 
  210. continue; 
  211.   
  212.   
  213. activate_locked: 
  214. //將頁(yè)面設(shè)為active頁(yè)面,等回去將其放入lru的active鏈表 
  215. SetPageActive(page); 
  216. pgactivate++; 
  217. keep_locked: 
  218. //保持頁(yè)面的狀態(tài)不變,放入對(duì)應(yīng)的lru active或inactive鏈表中 
  219. unlock_page(page); 
  220. keep: 
  221. //將該無(wú)法回收的頁(yè)面,放入到ret_pages鏈表中 
  222. list_add(&page->lru, &ret_pages); 
  223. BUG_ON(PageLRU(page)); 
  224. //此處將無(wú)法回收的頁(yè)面放入page_list中,在函數(shù)返回后,去其進(jìn)行處理 
  225. list_splice(&ret_pages, page_list); 
  226. //此處將可以釋放的頁(yè)面通通給釋放掉,回收了^_^ 
  227.  
  228. if (pagevec_count(&freed_pvec)) 
  229. __pagevec_release_nonlru(&freed_pvec); 
  230. mod_page_state(pgactivate, pgactivate); 
  231. sc->nr_reclaimed += reclaimed; 
  232. return reclaimed; 

 

責(zé)任編輯:奔跑的冰淇淋 來(lái)源: CSDN博客
相關(guān)推薦

2025-03-26 00:00:05

2010-06-02 13:00:43

Linux 內(nèi)存監(jiān)控

2011-08-15 16:28:06

Cocoa內(nèi)存管理

2021-09-05 18:29:58

Linux內(nèi)存回收

2023-10-18 13:31:00

Linux內(nèi)存

2013-10-12 13:01:51

Linux運(yùn)維內(nèi)存管理

2023-05-31 09:00:00

2010-09-26 16:42:04

JVM內(nèi)存組成JVM垃圾回收

2023-02-28 07:56:07

V8內(nèi)存管理

2009-09-02 09:23:26

.NET內(nèi)存管理機(jī)制

2023-12-19 21:52:51

Go垃圾回收開(kāi)發(fā)

2017-05-18 16:30:29

Linux內(nèi)存管理

2022-08-08 08:31:00

Linux內(nèi)存管理

2019-06-24 19:00:09

JavaScript內(nèi)存泄漏垃圾回收

2013-10-12 11:15:09

Linux運(yùn)維內(nèi)存管理

2025-04-15 06:00:00

2025-01-06 08:00:09

2010-09-27 13:41:22

JVM內(nèi)存回收

2013-04-01 10:07:19

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

2023-03-14 11:00:05

過(guò)期策略Redis
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)