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

Linux內(nèi)存管理--伙伴系統(tǒng)和內(nèi)存分配器

系統(tǒng) Linux
Linux內(nèi)核使用伙伴系統(tǒng)來解決內(nèi)存分配引起的外部碎片問題?;锇橄到y(tǒng)分配器大體上分為兩類。__get_free_pages()類函數(shù)返回分配的第一個頁面的線性地址;alloc_pages()類函數(shù)返回頁面描述符地址。

Linux內(nèi)核使用伙伴系統(tǒng)來解決內(nèi)存分配引起的外部碎片問題。伙伴系統(tǒng)分配器大體上分為兩類。__get_free_pages()類函數(shù)返回分配的第一個頁面的線性地址;alloc_pages()類函數(shù)返回頁面描述符地址。不管以哪種函數(shù)進行分配,最終會調(diào)用alloc_pages()進行分配頁面。

為清楚了解其分配制度,先給個伙伴系統(tǒng)數(shù)據(jù)的存儲框圖。

也就是每個order對應(yīng)一個free_area結(jié)構(gòu),free_area以不同的類型以鏈表的方式存儲這些內(nèi)存塊。


一、頁框的管理

所有的頁框描述符都存放在mem_map數(shù)組中。

1、page數(shù)據(jù)結(jié)構(gòu)

  1. struct page {    
  2.     page_flags_t flags; //標志                        
  3.     atomic_t _count;//該頁框的引用計數(shù),該引用計數(shù)為-1時表示該頁框是個空閑頁框     
  4.     atomic_t _mapcount;//頁框在頁表項中的數(shù)目,即該頁框在多少個頁表中被引用為頁表                    項    
  5.     unsigned long  private;//可用于多種內(nèi)核成分,若該頁為緩沖區(qū)所組成的頁,其用來組                        織緩沖區(qū)首部鏈表            
  6.     struct address_space  *mapping;//當頁面屬于頁高速緩存時指向?qū)?yīng)文件的 address _space ,當頁面屬于匿名區(qū)時,指向anon_vma描述符,anon_vma中有鏈表鏈接所有的vm_area_struct.    
  7.     pgoff_t index;  //存放換出頁標識符或者在頁磁盤映像或者匿名區(qū)中的數(shù)據(jù)塊的位置     
  8.     struct list_head lru; //將頁面鏈入最近最少使用鏈表中          
  9. };   

2、頁框管理區(qū)

  1. struct zone {    
  2.     unsigned long       free_pages;//管理區(qū)空閑頁的數(shù)目    
  3.     unsigned long       pages_min, pages_low, pages_high;    
  4.     //pages_min記錄了管理區(qū)保留頁的數(shù)目,pages_low,pages_high    
  5.     //分別是用于頁面回收的閾值    
  6.     struct per_cpu_pageset  pageset[NR_CPUS];//pageset用于各個cpu的單一頁面緩存,用于伙伴系統(tǒng)    
  7.     spinlock_t      lock; //用于保護的自旋鎖    
  8.     struct free_area    free_area[MAX_ORDER];//標示出管理區(qū)的空閑頁面,用于伙伴系統(tǒng)    
  9.     spinlock_t      lru_lock;   //用于保護lru鏈表的自旋鎖    
  10.     struct list_head     active_list;//記錄管理區(qū)內(nèi)處于active狀態(tài)頁面的鏈表    
  11.     struct list_head     inactive_list;//記錄管理區(qū)內(nèi)處于inactive狀態(tài)頁面的鏈表    
  12.     unsigned long       nr_scan_active;//回收時需要掃描的活躍頁數(shù)目,與頁面回收掃描時的優(yōu)先級有關(guān)    
  13.     unsigned long       nr_scan_inactive;//回收時需要掃描的非活躍頁數(shù)目    
  14.     unsigned long       nr_active;//管理區(qū)內(nèi)活躍頁的數(shù)目    
  15.     unsigned long       nr_inactive;//管理區(qū)內(nèi)非活躍頁的數(shù)目    
  16.     unsigned long       pages_scanned;//回收頁框時使用的計數(shù)器    
  17.     int         all_unreclaimable;//管理區(qū)內(nèi)都是不能回收的頁面時,置此標記位    
  18.     int temp_priority;  //當前回收頁面時的優(yōu)先級    
  19.     int prev_priority; //上次回收頁面時的優(yōu)先級    
  20.     wait_queue_head_t   *  wait_table;//進程等待隊列的散列表,可能進程正在等待管理區(qū)中的某頁    
  21.     unsigned long       wait_table_size;//散列表的大小    
  22.     unsigned long       wait_table_bits;//散列表數(shù)組大小    
  23.     struct pglist_data  *zone_pgdat;//管理區(qū)所屬的內(nèi)存節(jié)點    
  24.     struct page     *zone_mem_map;//指向管理區(qū)的第一個頁描述符    
  25.     unsigned long       zone_start_pfn;//管理區(qū)第一個頁框的下標    
  26.     unsigned long       spanned_pages;//以頁為單位的管理區(qū)的大小    
  27.     unsigned long       present_pages;//以頁為單位的管理區(qū)的大小,不包括洞    
  28.     char            *name;//管理區(qū)名稱    
  29. } ____cacheline_maxaligned_in_smp;  

3、內(nèi)存節(jié)點

Linux2.6支持numa的計算機模型,在這種模型中,cpu對不同的內(nèi)存單元的存取訪問時間是不一樣的,系統(tǒng)的物理內(nèi)存被劃分為幾個節(jié)點,在給定的節(jié)點內(nèi),任一給定的cpu對內(nèi)存的訪問時間都是一樣的,當然不同的cpu訪問該節(jié)點的內(nèi)存其訪問時間是不一樣的,內(nèi)核需要把cpu訪問節(jié)點的時間降到最小,這就要小心的選擇cpu常用內(nèi)核數(shù)據(jù)所在的內(nèi)存節(jié)點的位置。

節(jié)點描述符用pg_data_t數(shù)據(jù)結(jié)構(gòu)來表示。

  1. typedef struct pglist_data {   
  2.     struct zone node_zones[MAX_NR_ZONES];//該節(jié)點上內(nèi)存管理區(qū)數(shù)組   
  3.     struct zonelist  node_zonelists[GFP_ZONETYPES];//zonelist數(shù)組,用于內(nèi)存分配器進行內(nèi)存分配   
  4.     int nr_zones;//內(nèi)存管理區(qū)的個數(shù)   
  5.     struct page *node_mem_map;//該節(jié)點內(nèi)存的起始內(nèi)存描述符   
  6.     struct bootmem_data *bdata;   
  7.     unsigned long node_start_pfn;//該節(jié)點內(nèi)存的起始內(nèi)存的下標   
  8.     unsigned long node_present_pages;  //節(jié)點的大小,不包括洞   
  9.     unsigned long node_spanned_pages;  //節(jié)點的大小,包括洞   
  10.     int node_id;//節(jié)點id   
  11.     struct pglist_data *pgdat_next;//用于組成內(nèi)存節(jié)點鏈表   
  12.     wait_queue_head_t     kswapd_wait;//kswapd進程使用的等待隊列,平時kswapd會睡眠在該隊列上   
  13.     struct task_struct *kswapd;   //該內(nèi)存節(jié)點上用于內(nèi)存回收的kswapd內(nèi)存描述符,該進程在alloc_page內(nèi)存不足時,會被激活   
  14. } pg_data_t;  

下面的圖表明了內(nèi)存節(jié)點,內(nèi)存管理區(qū)和page之間的關(guān)系,page->flag的低幾位保存了node id和zone id。


二、伙伴系統(tǒng)

通常我們用alloc_pages(), alloc_page(), __get_free_pages(), __get_free_page(), get_zeroed_page() 來獲取頁框,前兩個返回的是頁描述符的地址,后面兩個返回的是物理頁的線性地址。高端內(nèi)存的分配只能通過前兩個分配函數(shù),后面兩個由于是線性地址,不可以直接使用。這四個函數(shù)接口最終都會調(diào)用到伙伴系統(tǒng)的相關(guān)接口進行內(nèi)存的分配工作。

linux內(nèi)核的伙伴算法最大限度的減少了內(nèi)存的碎片,其實應(yīng)該說成是盡自己最大的努力減少了內(nèi)存的碎片。其思想就是將物理內(nèi)存分成11個塊鏈表,每個鏈表包含的是大小為1,2,4,8...512,1024的連續(xù)頁框塊。舉例來說要分配256個連續(xù)頁框,會先到塊大小為256的鏈表中查找空閑塊,若有直接返回,若沒有,去大小為512的鏈表中進行查找,將512大小塊分為兩部分,一部分返回,一部分插入256大小的鏈表中,若512大小的鏈表中還沒有,到1024大小的鏈表中查找,取出256大小的塊,將剩下的512,256的塊分別插入到各個鏈表中,內(nèi)存釋放的過程則是相反的。

在分配過程中由大塊分解而成的小塊中沒有被分配的塊將一直等著被分配的塊被釋放從而和其合并,合并的操作正是在頁面釋放的過程中,最終的結(jié)果就是相當與沒有分解大塊,伙伴系統(tǒng)一直在向這個結(jié)果收斂,這就是為何伙伴系統(tǒng)能避免碎片的原因?;锇橄到y(tǒng)在分配和釋放兩個方向上執(zhí)行分解和合并兩個互逆的操作,如果一開始系統(tǒng)沒有碎片,那么最終的碎片將最小化,因為互逆的操作將最大力度的抵消碎片的產(chǎn)生,這就是精髓了。

伙伴系統(tǒng)的算法主要使用了zone描述符中的zone_mem_map字段和free_area數(shù)組,zone_mem_map指向了內(nèi)存管理區(qū)的物理頁的起始地址,free_area則代表著我們上面提到的11個鏈表。

  1. struct free_area { 
  2. struct list_head 
  3. free_list;//塊鏈表頭 
  4. unsigned long  *map;//塊分配的位圖 
  5. }; 

struct free_area中的free_list字段是空閑內(nèi)存塊的鏈表頭,空閑內(nèi)存塊通過內(nèi)存描述符的lru字段鏈接成鏈表,一個大小為1024頁框的內(nèi)存塊中,其第一個頁框的private字段保存了對應(yīng)的order即10,第一個頁框也有一個標記位,表明是否是buddy的內(nèi)存塊。在頁框被釋放時用該字段判斷是否可以和它的伙伴內(nèi)存塊合為更大的內(nèi)存塊。

1、內(nèi)存塊的分配

  1. static struct page *__rmqueue(struct zone *zone, unsigned int order)   
  2. {   
  3.     struct free_area * area;   
  4.     unsigned int current_order;   
  5.     struct page *page;   
  6.     //從order大小的內(nèi)存塊鏈表開始進行遍歷   
  7.     for (current_order = order; current_order < MAX_ORDER; ++current_order) {   
  8.         //找到current_order對應(yīng)的內(nèi)存塊鏈表   
  9.         area = zone->free_area + current_order;   
  10.         //內(nèi)存塊鏈表為空,繼續(xù)下一個內(nèi)存塊鏈表   
  11.         if (list_empty(&area->free_list))   
  12.             continue;   
  13.         //通過內(nèi)存塊包含的第一個page的lru將其從空閑鏈表上摘鏈   
  14.         page = list_entry(area->free_list.next, struct page, lru);   
  15.         list_del(&page->lru);   
  16.         //清除page的buddy標記,將private置為0   
  17.         rmv_page_order(page);   
  18.         area->nr_free--;   
  19.         zone->free_pages -= 1UL << order;   
  20.         //將current_order大小的內(nèi)存塊刨去order大小后,將其余的內(nèi)存頁面分布到   
  21.         //各個空閑鏈表中   
  22.         expand(zone, page, order, current_order, area);   
  23.         return page;   
  24.     }   
  25.     return NULL;   
  26. }   

2、內(nèi)存塊的釋放

  1. static inline void __free_one_page(struct page *page,   
  2.         struct zone *zone, unsigned int order)   
  3. {   
  4.     unsigned long page_idx;   
  5.     int order_size = 1 << order;   
  6.     if (unlikely(PageCompound(page)))   
  7.         destroy_compound_page(page, order);   
  8.     page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1);   
  9.     //增加空閑頁面的值   
  10.     zone->free_pages += order_size;   
  11.     while (order < MAX_ORDER-1) {   
  12.         unsigned long combined_idx;   
  13.         struct free_area *area;   
  14.         struct page *buddy;   
  15.         //找到對應(yīng)的伙伴塊   
  16.         buddy = page + (page_idx^(1<<order) -page_idx);   
  17.         //判斷頁面與伙伴塊能否合為一個內(nèi)存塊   
  18.         if (!page_is_buddy(page, buddy, order))   
  19.             break;      /* Move the buddy up one level. */   
  20.         //將伙伴塊從空閑鏈表上摘除掉   
  21.         list_del(&buddy->lru);   
  22.         area = zone->free_area + order;   
  23.         area->nr_free--;   
  24.         rmv_page_order(buddy);   
  25.         //得到合并后的內(nèi)存塊的page index   
  26.         combined_idx = __find_combined_index(page_idx, order);   
  27.         //獲取合并后內(nèi)存塊的起始page   
  28.         page = page + (combined_idx - page_idx);   
  29.         page_idx = combined_idx;   
  30.         order++;   
  31.     }   
  32.     //設(shè)置page->private字段,表明內(nèi)存塊的大小   
  33.     set_page_order(page, order);   
  34.     //插入到對應(yīng)內(nèi)存塊的空閑鏈表中   
  35.     list_add(&page->lru, &zone->free_area[order].free_list);   
  36.     zone->free_area[order].nr_free++;   
  37. }   

3、每CPU頁框高速緩存

系統(tǒng)中很多情況下只是去申請單一的頁框,提供一個單個頁框的本地高速緩存對于提高單一頁框的效率會有很大的幫助,每個內(nèi)存管理區(qū)都定義了一個每cpu的頁框高速緩存,每個高速緩存都會存放一些頁框,用于滿足本地cpu發(fā)出的分配單一頁框的請求。

每個內(nèi)存緩沖區(qū)為每個cpu提供了2個頁框高速緩存:熱頁框高速緩存和冷頁框高速緩存,熱高速緩存用于大多數(shù)的情況,如果獲取到頁面,cpu立刻進行讀或者寫操作的話,可以改善硬件高速緩存的使用效率,冷高速緩存通常用與dma傳輸?shù)惹闆r,這種情況無需使用硬件高速緩存。

每cpu頁框高速緩存的主要數(shù)據(jù)結(jié)構(gòu)在zone的pageset字段,類型為per_cpu_pageset,

  1. struct per_cpu_pageset { 
  2. struct per_cpu_pages pcp[2]; 
  3. /* 0: hot.  1: cold */ 
  4. struct per_cpu_pages { 
  5. int count;  //本地高速緩存頁面的個數(shù) 
  6. int high;  //高水位,當本地高速緩存頁面數(shù)超過high時,即需要清空 
  7. int batch;  //沒有頁框,或者頁框數(shù)超過high時,向伙伴系統(tǒng)申請或者釋放batc個 頁框 
  8. struct list_head list; 
  9. //本地高速緩存頁框鏈表 
  10. }; 

分配單一頁框使用的內(nèi)核函數(shù)是buffered_rmqueue()

  1. static struct page *buffered_rmqueue(struct zonelist *zonelist,   
  2.             struct zone *zone, int order, gfp_t gfp_flags)   
  3. {   
  4.     unsigned long flags;   
  5.     struct page *page;   
  6.     //獲取標記位,決定從冷還是熱本地頁框高速緩存中獲取頁面   
  7.     int cold = !!(gfp_flags & __GFP_COLD);   
  8.     int cpu;   
  9.    
  10. again:   
  11.     //獲取本地cpu   
  12.     cpu  = get_cpu();   
  13.     //分配單一頁面,到高速緩存中獲取   
  14.     if (likely(order == 0)) {   
  15.         struct per_cpu_pages *pcp;   
  16.         pcp = &zone_pcp(zone, cpu)->pcp[cold];   
  17.         local_irq_save(flags);   
  18.         //本地高速緩存中已經(jīng)沒有了,填充本地高速緩存   
  19.         if (!pcp->count) {   
  20.             //到伙伴系統(tǒng)中分配頁面填充到本地高速緩存,每次去伙伴系統(tǒng)中   
  21.             //分配1個頁面,分配batch各頁面為止   
  22.             pcp->count += rmqueue_bulk(zone, 0,   
  23.                         pcp->batch, &pcp->list);   
  24.             if (unlikely(!pcp->count))   
  25.                 goto failed;   
  26.         }   
  27.         //從本地高速緩存鏈表上摘除page   
  28.         page = list_entry(pcp->list.next, struct page, lru);   
  29.         list_del(&page->lru);   
  30.         pcp->count--;   
  31.     } else {   
  32.         //非分配單一頁面,使用__rmqueue()直接去分配即可   
  33.         spin_lock_irqsave(&zone->lock, flags);   
  34.         page = __rmqueue(zone, order);   
  35.         spin_unlock(&zone->lock);   
  36.         if (!page)   
  37.             goto failed;   
  38.     }   
  39.    
  40.     local_irq_restore(flags);   
  41.     put_cpu();   
  42.     :   
  43.     return page;   
  44. failed:   
  45.     local_irq_restore(flags);   
  46.     put_cpu();   
  47.     return NULL;   
  48. }   
  49.     釋放單一頁框的函數(shù)是free_hot_cold_page():   
  50. static void fastcall free_hot_cold_page(struct page *page, int cold)   
  51. {   
  52.     //通過page->flag低幾位得到其所處的zone   
  53.     struct zone *zone = page_zone(page);   
  54.     struct per_cpu_pages *pcp;   
  55.     unsigned long flags;   
  56.    
  57.     kernel_map_pages(page, 1, 0);   
  58.     //根據(jù)冷熱標記找到對應(yīng)的本地頁框高速緩存對應(yīng)的鏈表   
  59.     pcp = &zone_pcp(zone, get_cpu())->pcp[cold];   
  60.     local_irq_save(flags);   
  61.     __count_vm_event(PGFREE);   
  62.     //將頁面插入對應(yīng)的鏈表   
  63.     list_add(&page->lru, &pcp->list);   
  64.     pcp->count++;   
  65.     //若本地頁框高速緩存的頁面數(shù)目超過了pcp->high,將batch個頁面   
  66.     //歸還給伙伴系統(tǒng)   
  67.     if (pcp->count >= pcp->high) {   
  68.         free_pages_bulk(zone, pcp->batch, &pcp->list, 0);   
  69.         pcp->count -= pcp->batch;   
  70.     }   
  71.     local_irq_restore(flags);   
  72.     put_cpu();   
  73. }   

三、內(nèi)存管理區(qū)分配器

內(nèi)存管理區(qū)分配器需要滿足下列的幾個目標:

  1. 應(yīng)該保護需要保留的內(nèi)存池。
  2. 當頁框不足且允許進程阻塞時,應(yīng)該觸發(fā)頁框回收算法,當一些頁框被釋放出來,再次進行分配。
  3. 在盡可能的情況下,保留小而珍貴的ZONE_DMA區(qū)的內(nèi)存。

__alloc_pages()

  1. struct page * fastcall   
  2. __alloc_pages(gfp_t gfp_mask, unsigned int order,   
  3.         struct zonelist *zonelist)   
  4. {   
  5.     const gfp_t wait = gfp_mask & __GFP_WAIT;   
  6.     struct zone **z;   
  7.     struct page *page;   
  8.     struct reclaim_state reclaim_state;   
  9.     struct task_struct *p = current;   
  10.     int do_retry;   
  11.     int alloc_flags;   
  12.     int did_some_progress;   
  13.     might_sleep_if(wait);   
  14.    
  15. restart:   
  16.     //zonelist->zones代表了內(nèi)存分配時使用內(nèi)存管理區(qū)的順序   
  17.     z = zonelist->zones;     
  18.    
  19.     //一開始先以zone->page_low做為基準進行內(nèi)存分配   
  20.     page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, order,   
  21.                 zonelist, ALLOC_WMARK_LOW|ALLOC_CPUSET);   
  22.     //分配到頁框,退出     
  23.     if (page)   
  24.         goto got_pg;   
  25.     //沒有分配到頁框,   喚醒kswapd進行內(nèi)存回收   
  26.     do {   
  27.         wakeup_kswapd(*z, order);   
  28.     } while (*(++z));   
  29.    
  30.     //調(diào)用kswapd進程回收內(nèi)存后,進行第二次回收,頁面基準值為zone->min_pages,   
  31.     alloc_flags = ALLOC_WMARK_MIN;   
  32.     //當前進程是實時進程且不在中斷中或者進程不允許等待,內(nèi)存分配會比較   
  33.     //緊迫,需要置ALLOC_HARDER標記進一步來縮小內(nèi)存基準值   
  34.     if ((unlikely(rt_task(p)) && !in_interrupt()) || !wait)   
  35.         alloc_flags |= ALLOC_HARDER;   
  36.     //__GFP_HIGH標志著允許訪問緊急內(nèi)存池       
  37.     if (gfp_mask & __GFP_HIGH)   
  38.         alloc_flags |= ALLOC_HIGH;   
  39.     //通常GFP_ATOMIC是不允許進程休眠來等待內(nèi)存釋放的   
  40.     if (wait)   
  41.         alloc_flags |= ALLOC_CPUSET;   
  42.    
  43.     //再次獲取頁面   
  44.     page = get_page_from_freelist(gfp_mask, order, zonelist, alloc_flags);   
  45.     //獲取到了,OK,退出   
  46.     if (page)   
  47.         goto got_pg;   
  48.    
  49.     //還是無法獲取頁面,系統(tǒng)內(nèi)存肯定不足,在非中斷和軟中斷的情況下,進行第三次掃描   
  50.     //PF_MEMALLOC標記的意思是不想讓內(nèi)存分配失敗,可以動用緊急內(nèi)存   
  51.     if (((p->flags & PF_MEMALLOC) || unlikely(test_thread_flag(TIF_MEMDIE)))   
  52.             && !in_interrupt()) {   
  53.         //__GFP_NOMEMALLOC表明了不允許沒有內(nèi)存分配,進行第三次掃描,第三次掃描繪忽略閾值判斷,   
  54.         //動用緊急內(nèi)存   
  55.         if (!(gfp_mask & __GFP_NOMEMALLOC)) {   
  56. nofail_alloc:   
  57.             /* go through the zonelist yet again, ignoring mins */   
  58.             page = get_page_from_freelist(gfp_mask, order,   
  59.                 zonelist, ALLOC_NO_WATERMARKS);   
  60.             //分配到了頁面,退出   
  61.             if (page)   
  62.                 goto got_pg;   
  63.             //沒有分配到頁面,不允許失敗   
  64.             if (gfp_mask & __GFP_NOFAIL) {   
  65.                 blk_congestion_wait(WRITE, HZ/50);   
  66.                 goto nofail_alloc;   
  67.             }   
  68.         }   
  69.         goto nopage;   
  70.     }   
  71.    
  72.     //進程不允許睡眠等待,苦逼了,只能返回NULL了   
  73.     if (!wait)   
  74.         goto nopage;   
  75.    
  76. rebalance:   
  77.     //檢查是否有其它進程需要調(diào)度運行   
  78.     cond_resched();   
  79.    
  80.     /* We now go into synchronous reclaim */   
  81.     cpuset_memory_pressure_bump();   
  82.     //給進程設(shè)定PF_MEMALLOC標記位   
  83.     p->flags |= PF_MEMALLOC;   
  84.     reclaim_state.reclaimed_slab = 0;   
  85.     p->reclaim_state = &reclaim_state;   
  86.     //調(diào)用內(nèi)存回收函數(shù)進行內(nèi)存回收   
  87.     did_some_progress = try_to_free_pages(zonelist->zones, gfp_mask);   
  88.    
  89.     p->reclaim_state = NULL;   
  90.     p->flags &= ~PF_MEMALLOC;   
  91.     //內(nèi)存回收過程可能耗時太多,檢查是否有進程需要進行調(diào)度   
  92.     cond_resched();   
  93.     //如果內(nèi)存回收的確回收到了一些頁面   
  94.     if (likely(did_some_progress)) {   
  95.         //再次嘗試回收頁面   
  96.         page = get_page_from_freelist(gfp_mask, order,   
  97.                         zonelist, alloc_flags);   
  98.         if (page)   
  99.             goto got_pg;   
  100.     } else if ((gfp_mask & __GFP_FS) && !(gfp_mask & __GFP_NORETRY)) {   
  101.         //該情況下頁面回收并沒有回收到任何頁框,或許系統(tǒng)到了   
  102.         //危急的時刻了!!!殺死一個其他的進程,然后一切從頭再來!   
  103.         page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, order,   
  104.                 zonelist, ALLOC_WMARK_HIGH|ALLOC_CPUSET);   
  105.         if (page)   
  106.             goto got_pg;   
  107.    
  108.         out_of_memory(zonelist, gfp_mask, order);   
  109.         goto restart;   
  110.     }   
  111.    
  112.        
  113.     do_retry = 0;   
  114.     //gfp_mask標記中GFP_NORETRY表明了是否允許再次掃描內(nèi)存管理區(qū)   
  115.     if (!(gfp_mask & __GFP_NORETRY)) {   
  116.         //GFP_REPEAT,GFP_NOFAIL都要求反復(fù)對內(nèi)存管理區(qū)進行掃描   
  117.         if ((order <= 3) || (gfp_mask & __GFP_REPEAT))   
  118.             do_retry = 1;   
  119.         if (gfp_mask & __GFP_NOFAIL)   
  120.             do_retry = 1;   
  121.     }   
  122.     //需要再次嘗試內(nèi)存分配的話,調(diào)用blk_congestion_wait()等待一會,   
  123.     if (do_retry) {   
  124.         blk_congestion_wait(WRITE, HZ/50);   
  125.         goto rebalance;   
  126.     }   
  127.    
  128. nopage:   
  129.     if (!(gfp_mask & __GFP_NOWARN) && printk_ratelimit()) {   
  130.         dump_stack();   
  131.         show_mem();   
  132.     }   
  133. got_pg:   
  134.     return page;   
  135. }   
  136. 3.3.2get_page_from_freelist   
  137.     在代碼中到伙伴系統(tǒng)分配內(nèi)存的代碼實現(xiàn)實際在get_page_from_freelist()中。   
  138. static struct page *   
  139. get_page_from_freelist(gfp_t gfp_mask, unsigned int order,   
  140.         struct zonelist *zonelist, int alloc_flags)   
  141. {   
  142.     struct zone **z = zonelist->zones;   
  143.     struct page *page = NULL;   
  144.     int classzone_idx = zone_idx(*z);   
  145.     do {   
  146.         if ((alloc_flags & ALLOC_CPUSET) &&   
  147.                 !cpuset_zone_allowed(*z, gfp_mask))   
  148.             continue;   
  149.    
  150.         if (!(alloc_flags & ALLOC_NO_WATERMARKS)) {   
  151.             unsigned long mark;   
  152.             //alloc_flags決定了mark的大小,mark是衡量是否可以從該內(nèi)存管理區(qū)分配頁框的標準   
  153.             //用于下面的zone_watermark_ok   
  154.             if (alloc_flags & ALLOC_WMARK_MIN)   
  155.                 mark = (*z)->pages_min;   
  156.             else if (alloc_flags & ALLOC_WMARK_LOW)   
  157.                 mark = (*z)->pages_low;   
  158.             else   
  159.                 mark = (*z)->pages_high;   
  160.             //zone中的剩余內(nèi)存不能達到標準,需要回收內(nèi)存   
  161.             if (!zone_watermark_ok(*z, order, mark,   
  162.                     classzone_idx, alloc_flags))   
  163.                 if (!zone_reclaim_mode ||   
  164.                     !zone_reclaim(*z, gfp_mask, order))   
  165.                     continue;   
  166.         }   
  167.         //調(diào)用bufferd_rmqueue向伙伴系統(tǒng)申請內(nèi)存頁框   
  168.         page = buffered_rmqueue(zonelist, *z, order, gfp_mask);   
  169.         if (page) {   
  170.             break;   
  171.         }   
  172.     } while (*(++z) != NULL);   
  173.     return page;   
  174. }   
  175. 3.3.3 zone_watermark_ok   
  176. int zone_watermark_ok(struct zone *z, int order, unsigned long mark,   
  177.               int classzone_idx, int alloc_flags)   
  178. {   
  179.     //基準值是mark,可以使zone->low,high,min中的任何一個值   
  180.     long min = mark, free_pages = z->free_pages - (1 << order) + 1;   
  181.     int o;   
  182.     //如果__GFP_WAIT被置位,通常ALLOC_HIGH被置位,將最小值減去1/2   
  183.     if (alloc_flags & ALLOC_HIGH)   
  184.         min -= min / 2;   
  185.     //如果__GFP_WAIT被置位,如果當前進程是一個實時進程并且已經(jīng)在進程上下文中,已經(jīng)完成了   
  186.     //內(nèi)存分配,則alloc_flag中的ALLOC_HARDER被置位,min再減少1/4   
  187.     if (alloc_flags & ALLOC_HARDER)   
  188.         min -= min / 4;   
  189.     //空閑頁面少于min 加上管理區(qū)的保留頁面數(shù)   
  190.     if (free_pages <= min + z->lowmem_reserve[classzone_idx])   
  191.         return 0;   
  192.     //對于0-order個大小的頁面空閑鏈表,對于每個鏈表,都   
  193.     //對于order為k的塊至少有min/2(k)個頁框   
  194.     for (o = 0; o < order; o++) {   
  195.         /* At the next order, this order's pages become unavailable */   
  196.         free_pages -= z->free_area[o].nr_free << o;   
  197.    
  198.         /* Require fewer higher order pages to be free */   
  199.         min >>= 1;   
  200.    
  201.         if (free_pages <= min)   
  202.             return 0;   
  203.     }   
  204.     return 1;      
  205. }   

 

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

2023-04-03 08:25:02

Linux內(nèi)存slub

2024-12-11 08:18:11

2020-12-15 08:54:06

Linux內(nèi)存碎片化

2024-10-11 10:00:20

2025-04-11 00:44:00

2023-10-18 13:31:00

Linux內(nèi)存

2021-08-03 09:02:58

LinuxSlab算法

2024-05-06 08:09:10

Linux內(nèi)存管理

2020-03-11 13:44:20

編程語言PythonJava

2009-12-25 15:34:54

slab分配器

2017-02-08 08:40:21

C++固定內(nèi)存塊

2017-01-17 16:17:48

C++固定分配器

2017-01-20 14:21:35

內(nèi)存分配器存儲

2013-10-14 10:41:41

分配器buddy syste

2020-07-07 07:57:39

Linux內(nèi)存碎片化

2013-10-12 13:01:51

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

2014-09-01 10:09:44

Linux

2022-02-23 16:49:19

Linux內(nèi)存數(shù)據(jù)結(jié)構(gòu)

2025-02-10 07:30:00

malloc內(nèi)存分配器內(nèi)存

2023-12-22 07:55:38

Go語言分配策略
點贊
收藏

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