linux內存夥伴算法(分配頁)

夥伴系統中用於分配頁的函數以下:node

alloc_pages(mask,order)分配2^order頁並返回一個struct page的實例,表示分配的內存塊的起始頁。alloc_page(mask)是前者在order=0狀況下的簡化形式,只分配一頁。linux

get_zeroed_page(mask)分配一頁並返回一個page實例,頁對應的內存填充0(全部其餘函數分配以後的內容是未定義的)。算法

__get_free_pages(mask,order)和__get_free_page(mask)的工做方式與上述函數相同,但返回分配內存塊的虛擬地址,而不是page實例。緩存

get_dma_pages(gfp_mask,order)用來得到適用於DMA的頁。app

在空閒內存沒法知足請求以致於分配失敗的狀況下,全部上述函數都返回空指針 (alloc_pages和alloc_page)或者0(get_zeroed_page、__get_free_pages和 __get_free_page)。所以內核在各次分配以後必須檢查返回的結果。這種慣例與設計得很好的用戶層應用程序沒有什麼不一樣,但在內核中忽略檢查 將會致使嚴重得多的故障。less

前述全部函數中使用的mask參數的語義是什麼?linux將內核劃分爲內存域,內核提供了所謂的內存域修飾符,來指定從哪一個內存域分配所需的頁。函數

  1. #define __GFP_DMA   ((__force gfp_t)0x01u)  
  2. #define __GFP_HIGHMEM   ((__force gfp_t)0x02u)  
  3. #define __GFP_DMA32 ((__force gfp_t)0x04u)  
除了內存域修飾符以外,掩碼中還能夠設置一些標誌,這些額外的標誌並不限制從哪一個物理內存段分配內存,但確實能夠改變分配器的行爲。
  1. #define __GFP_WAIT  ((__force gfp_t)0x10u)  //表示分配內存的請求能夠中斷。也就是說,調度器在該請求期間可隨意選擇另外一個過程執行,或者該請求能夠被另外一個更重要的事件中斷。  
  2. #define __GFP_HIGH  ((__force gfp_t)0x20u)  //若是請求很是重要,則設置__GFP_HIGH,即內核急切的須要內存時。在分配內存失敗可能給內核帶來嚴重得後果時,通常會設置該標誌  
  3. #define __GFP_IO    ((__force gfp_t)0x40u)  //在查找空閒內存期間內核能夠進行I/O操做。這意味着若是內核在內存分配期間換出頁,那麼僅當設置該標誌時,才能將選擇的頁寫入磁盤。  
  4. #define __GFP_FS    ((__force gfp_t)0x80u)  //容許內核執行VFS操做  
  5. #define __GFP_COLD  ((__force gfp_t)0x100u) //若是須要分配不在CPU高速緩存中的「冷」頁時,則設置__GFP_COLD。  
  6. #define __GFP_NOWARN    ((__force gfp_t)0x200u) //在分配失敗時禁止內核故障警告。  
  7. #define __GFP_REPEAT    ((__force gfp_t)0x400u) //在分配失敗後自動重試,但在嘗試若干次以後會中止。  
  8. #define __GFP_NOFAIL    ((__force gfp_t)0x800u) //在分配失敗後一直重試,直至成功。  
  9. #define __GFP_NORETRY   ((__force gfp_t)0x1000u)//不重試,可能失敗  
  10. #define __GFP_COMP  ((__force gfp_t)0x4000u)//增長複合頁元數據  
  11. #define __GFP_ZERO  ((__force gfp_t)0x8000u)//在分配成功時,將返回填充字節0的頁。  
  12. #define __GFP_NOMEMALLOC ((__force gfp_t)0x10000u) //不適用緊急分配鏈表  
  13. #define __GFP_HARDWALL   ((__force gfp_t)0x20000u) // 只在NUMA系統上有意義。它限制只在分配到當前進程的各個CPU所關聯的結點分配內存。若是進程容許在全部的CPU上運行(默認狀況下),該標誌是沒有 意義的。只有進程能夠運行的CPU受限時,該標誌纔有意義。  
  14. #define __GFP_THISNODE  ((__force gfp_t)0x40000u)//頁只在NUMA系統上有意義,若是設置該比特位,則內存分配失敗的狀況下不容許使用其餘結點做爲備用,須要保證在當前結點或者明確指定的結點上成功分配內存。  
  15. #define __GFP_RECLAIMABLE ((__force gfp_t)0x80000u) //將分配的內存標記爲可回收  
  16. #define __GFP_MOVABLE   ((__force gfp_t)0x100000u)  //將分配的內存標記爲可移動  
  17.   
  18. #define __GFP_BITS_SHIFT 21 /* Room for 21 __GFP_FOO bits */  
  19. #define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))  
  20.   
  21. /* This equals 0, but use constants in case they ever change */  
  22. #define GFP_NOWAIT  (GFP_ATOMIC & ~__GFP_HIGH)  
因爲這些標誌老是組合使用,內核作了一些分組,包含了用於各類標準情形的適當地標誌。
  1. #define GFP_ATOMIC  (__GFP_HIGH)//用於原子分配,在任何狀況下都不能中斷,可能使用緊急分配鏈表中的內存  
  2. #define GFP_NOIO    (__GFP_WAIT)//明確禁止IO操做,但能夠被中斷  
  3. #define GFP_NOFS    (__GFP_WAIT | __GFP_IO)//明確禁止訪問VFS層操做,但能夠被中斷  
  4. #define GFP_KERNEL  (__GFP_WAIT | __GFP_IO | __GFP_FS)//內核分配的默認配置  
  5. #define GFP_TEMPORARY   (__GFP_WAIT | __GFP_IO | __GFP_FS | \  
  6.              __GFP_RECLAIMABLE)  
  7. #define GFP_USER    (__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HARDWALL)//用戶分配的默認配置  
  8. #define GFP_HIGHUSER    (__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HARDWALL | \  
  9.              __GFP_HIGHMEM)//是GFP_USER的一個擴展,頁用於用戶空間,它容許分配沒法直接映射的高端內存,使用高端內存頁是沒有壞處的,由於用戶過程的地址空間老是經過非線性頁表組織的  
  10. #define GFP_HIGHUSER_MOVABLE    (__GFP_WAIT | __GFP_IO | __GFP_FS | \  
  11.                  __GFP_HARDWALL | __GFP_HIGHMEM | \  
  12.                  __GFP_MOVABLE)//相似於GFP_HIGHUSER,但分配是在虛擬內存域ZONE_MOVABLE中進行  
  13. #define GFP_NOFS_PAGECACHE  (__GFP_WAIT | __GFP_IO | __GFP_MOVABLE)  
  14. #define GFP_USER_PAGECACHE  (__GFP_WAIT | __GFP_IO | __GFP_FS | \  
  15.                  __GFP_HARDWALL | __GFP_MOVABLE)  
  16. #define GFP_HIGHUSER_PAGECACHE  (__GFP_WAIT | __GFP_IO | __GFP_FS | \  
  17.                  __GFP_HARDWALL | __GFP_HIGHMEM | \  
  18.                  __GFP_MOVABLE)  
  19.   
  20. #ifdef CONFIG_NUMA  
  21. #define GFP_THISNODE    (__GFP_THISNODE | __GFP_NOWARN | __GFP_NORETRY)  
  22. #else  
  23. #define GFP_THISNODE    ((__force gfp_t)0)  
  24. #endif  
  25.   
  26. /* This mask makes up all the page movable related flags */  
  27. #define GFP_MOVABLE_MASK (__GFP_RECLAIMABLE|__GFP_MOVABLE)  
  28.   
  29. /* Control page allocator reclaim behavior */  
  30. #define GFP_RECLAIM_MASK (__GFP_WAIT|__GFP_HIGH|__GFP_IO|__GFP_FS|\  
  31.             __GFP_NOWARN|__GFP_REPEAT|__GFP_NOFAIL|\  
  32.             __GFP_NORETRY|__GFP_NOMEMALLOC)  
  33.   
  34. /* Control allocation constraints */  
  35. #define GFP_CONSTRAINT_MASK (__GFP_HARDWALL|__GFP_THISNODE)  
  36.   
  37. /* Do not use these with a slab allocator */  
  38. #define GFP_SLAB_BUG_MASK (__GFP_DMA32|__GFP_HIGHMEM|~__GFP_BITS_MASK)  
  39.   
  40. /* Flag - indicates that the buffer will be suitable for DMA.  Ignored on some 
  41.    platforms, used as appropriate on others */  
  42.   
  43. #define GFP_DMA     __GFP_DMA  
  44.   
  45. /* 4GB DMA on some platforms */  
  46. #define GFP_DMA32   __GFP_DMA32  
  1. #define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)  
  1. #define __get_free_page(gfp_mask) \  
  2.         __get_free_pages((gfp_mask),0)  
  1. #define __get_dma_pages(gfp_mask, order) \  
  2.         __get_free_pages((gfp_mask) | GFP_DMA,(order))  
  1. fastcall unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)  
  2. {  
  3.     struct page * page;  
  4.     page = alloc_pages(gfp_mask, order);  
  5.     if (!page)  
  6.         return 0;  
  7.     return (unsigned long) page_address(page);  
  8. }  
  1. #define alloc_pages(gfp_mask, order) \  
  2.         alloc_pages_node(numa_node_id(), gfp_mask, order)  
根據上面的代碼,能夠得出各個分配函數之間的關係以下圖所示:


主要的函數是alloc_pages_node。alloc_pages_node源代碼的詳細分析以下:oop

  1. static inline struct page *alloc_pages_node(int nid, gfp_t gfp_mask,  
  2.                         unsigned int order)  
  3. {  
  4.     if (unlikely(order >= MAX_ORDER))//執行一個檢查,避免分配過大的內存塊  
  5.         return NULL;  
  6.   
  7.     /* Unknown node is current node */  
  8.     if (nid < 0)//若是指定負的結點ID(不存在),內核自動地使用當前執行CPU對應的結點ID。  
  9.         nid = numa_node_id();  
  10.   
  11.     return __alloc_pages(gfp_mask, order,  
  12.         NODE_DATA(nid)->node_zonelists + gfp_zone(gfp_mask));//接下來的工做委託給__alloc_pages,只需傳遞一組適當地參數,請注意,gfp_zone用於選擇分配內存的內存域。  
  13. }  
  1. static inline enum zone_type gfp_zone(gfp_t flags)//本函數比較好理解,就是根據指定的標誌肯定內存域  
  2. {  
  3.     int base = 0;  
  4.   
  5. #ifdef CONFIG_NUMA  
  6.     if (flags & __GFP_THISNODE)  
  7.         base = MAX_NR_ZONES;  
  8. #endif  
  9.   
  10. #ifdef CONFIG_ZONE_DMA  
  11.     if (flags & __GFP_DMA)  
  12.         return base + ZONE_DMA;  
  13. #endif  
  14. #ifdef CONFIG_ZONE_DMA32  
  15.     if (flags & __GFP_DMA32)  
  16.         return base + ZONE_DMA32;  
  17. #endif  
  18.     if ((flags & (__GFP_HIGHMEM | __GFP_MOVABLE)) ==  
  19.             (__GFP_HIGHMEM | __GFP_MOVABLE))  
  20.         return base + ZONE_MOVABLE;  
  21. #ifdef CONFIG_HIGHMEM  
  22.     if (flags & __GFP_HIGHMEM)  
  23.         return base + ZONE_HIGHMEM;  
  24. #endif  
  25.     return base + ZONE_NORMAL;  
  26. }  
__alloc_pages源代碼詳細分析以下:
  1. struct page * fastcall  
  2. __alloc_pages(gfp_t gfp_mask, unsigned int order,  
  3. <pre>       struct zonelist *zonelist)//<span style="text- align: justify; " lang="EN-US">gfp_mask</span>< span style="text-align: justify; ">是一些標誌位,用來制定如何尋找空閒頁框,< span style="text-align: justify; " lang="EN-US">order</span>& lt;span style="text-align: justify; ">用來表示所需物理塊的大小,從空閒鏈表中獲取</span& gt;<span style="text-align: justify; " lang="EN-US">2^order< /span><span style="text-align: justify; ">頁內存,< span style="text-align: justify; ">在管理區鏈表</span>< span style="text-align: justify; " lang="EN-US">zonelist</span& gt;<span style="text-align: justify; ">中依次查找每一個區,從中找到知足要求的區< /span></span></span></pre> {const gfp_t wait = gfp_mask & __GFP_WAIT;//gfp_mask是申請內存時用到的控制字,這一句 就是爲了檢測咱們的控制字裏面是否有__GPF_WAIT這個屬性struct zone **z;//<span style="text- align:justify">管理區結構 體</span>struct page *page;struct reclaim_state reclaim_state;struct task_struct *p =  
  4.  current;int do_retry;int alloc_flags;int did_some_progress;might_sleep_if(wait);// 若是在gfp_mask中設置了__GFP_WAIT位,代表內核能夠阻塞當前進程,來等待空閒頁面。在分配開始以前即阻塞,目的是爲了等待其它進程釋放 更多的頁面if (should_fail_alloc_page(gfp_mask, order))//經過簡單算法在真正分配前檢查分配是否會失 敗,避免進入真正的分配程序後浪費系統時間return NULL;restart:z  
  5.  = zonelist->zones; //zonelist 是struct node中的一個成員,它表示系統內全部normal內存頁區的鏈接鏈表,<span style="text- align:justify; text-indent:28px">首先 讓</span><span style="text-align:justify; text- indent:28px" lang="EN-US">z</span><span style="text- align:justify; text-indent:28px">指向第一個管理區</span>if  
  6.  (unlikely(*z == NULL)) {// 若是發現頭指針爲空,即沒有指向struct zone的有效指針,咱們就直接返回錯誤 /* * Happens if we have an empty zonelist as a result of * GFP_THISNODE being used on a memoryless node */return NULL;}page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, order,zonelist,  
  7.  ALLOC_WMARK_LOW|ALLOC_CPUSET);//get_page_from_freelist 以指定的watermark來分配頁面。<span style="text-indent:21pt">每一個zone struct中定義 了三個watermark:pages_min, pages_low, pages_high,表示zone中應保持的空閒頁面的閾 值。</span><span style="text-indent:21pt"> get_page_from_freelist函數經過設置Alloc  
  8.  flags來選擇watermark。</span><span style="text-indent:21pt"></span>if (page)// 首先以pages_low watermark分配頁面,若是分配成功,則跳轉到 got_pggoto got_pg;/* * GFP_THISNODE (meaning __GFP_THISNODE, __GFP_NORETRY and * __GFP_NOWARN set) should not cause reclaim since the subsystem  
  9.  * (f.e. slab) using GFP_THISNODE may choose to trigger reclaim * using a larger set of nodes after it has established that the * allowed per node queues are empty and that nodes are * over allocated. */if (NUMA_BUILD && (gfp_mask & GFP_THISNODE) == GFP_THISNODE)//若是pages_low watermark分配失敗的話,檢查gfp_mask,若是GFP_THISNODE標誌被設置,代表不能重試,所以跳轉到nopage,返回失敗goto  
  10.  nopage;for (z = zonelist->zones; *z; z++)wakeup_kswapd(*z, order);// 不然調用kswapd對zonelist中的全部zone進行頁面回收,期待能將一些閒置頁面交換到文件系統中 /* * OK, we're below the kswapd watermark and have kicked background * reclaim. Now things get more complex, so set up alloc_flags according  
  11.  * to how we want to proceed. * * The caller may dip into page reserves a bit more if the caller * cannot run direct reclaim, or if the caller has realtime scheduling * policy or is asking for __GFP_HIGH memory. GFP_ATOMIC requests will * set both ALLOC_HARDER  
  12.  (!wait) and ALLOC_HIGH (__GFP_HIGH). */alloc_flags = ALLOC_WMARK_MIN;// 設置alloc_flags的值,以page_min watermark來分配內存 if ((unlikely(rt_task(p)) && !in_interrupt()) || !wait)//倘若進程是非中 斷處理程序的實時進程,或者該進程不能被阻塞,那麼這個時候,我要在最低閾值的標準的基礎上,再次下降閾值 alloc_flags |= ALLOC_HARDER;if  
  13.  (gfp_mask & __GFP_HIGH)//<span style="text- align:justify">容許使用保留頁面</span><span style="text- align:justify" lang="EN-US">__GFP_HIGH</span>alloc_flags |= ALLOC_HIGH;if (wait)alloc_flags |= ALLOC_CPUSET;/* * Go through the zonelist again. Let __GFP_HIGH and allocations  
  14.  * coming from realtime tasks go deeper into reserves. * * This is the last chance, in general, before the goto nopage. * Ignore cpuset if GFP_ATOMIC (!wait) rather than fail alloc. * See also cpuset_zone_allowed() comment in kernel/cpuset.c. */page = get_page_from_freelist(gfp_mask,  
  15.  order, zonelist, alloc_flags);// 以指定的watermark來分配頁面,詳細討論見下文if (page)//分配成功,就進入got_pggoto got_pg; /* This allocation should allow future memory freeing. */rebalance://上面的 第二次分配失敗 if (((p->flags & PF_MEMALLOC) || unlikely(test_thread_flag(TIF_MEMDIE)))// 若是當前進程容許本次申請的內存能夠被釋放,而且不處於軟硬中斷的狀態,咱們不顧忌必須保留最小空閒內存頁,強行分配&&  
  16.  !in_interrupt()) {if (!(gfp_mask & __GFP_NOMEMALLOC)) {// 若是gfp_mask設置不須要保留緊急內存區域,以不設watermark再次分配頁面 nofail_alloc:/* go through the zonelist yet again, ignoring mins * /page = get_page_from_freelist(gfp_mask, order,zonelist, ALLOC_NO_WATERMARKS); //以不設watermark進行第三次分配if  
  17.  (page)// 第三次分配成功goto got_pg;if (gfp_mask & __GFP_NOFAIL) {//第三次分配失敗,若是 gfp_mask設置了__GFP_NOFAIL,則不斷重試,直到分配成功 congestion_wait(WRITE, HZ/50);goto nofail_alloc;}}goto nopage;} /* Atomic allocations - we can't balance anything */if (!wait) //<span style="text-align:justify; text-indent:28px">原子分配,不容許阻塞,則只 能返回失敗信號,分配失敗</span>goto  
  18.  nopage;cond_resched();// 從新調度以後,試圖釋放一些不經常使用的頁面/* We now go into synchronous reclaim * /cpuset_memory_pressure_bump();//開始進行同步內存回收p->flags |= PF_MEMALLOC;// 進程的標誌位設置爲PF_MEMALLOCreclaim_state.reclaimed_slab = 0;//對於再也不活躍的SLAB也給回收掉 p->reclaim_state = &reclaim_state;//改變進程回收的狀態did_some_progress  
  19.  = try_to_free_pages(zonelist->zones, order, gfp_mask);// 該函數選擇最近不十分活躍的頁,將其寫到交換區,在物理內存中騰出空間p->reclaim_state = NULL;p-> flags &= ~PF_MEMALLOC;cond_resched(); if (order != 0)drain_all_local_pages(); if (likely(did_some_progress)) {//<span style="background- color:rgb(240,243,250)">調度以後,若是確實釋放了一部分頁面,則從新分配頁面</span>page  
  20.  = get_page_from_freelist(gfp_mask, order,zonelist, alloc_flags);if (page)goto got_pg;} else if ((gfp_mask & __GFP_FS) && !(gfp_mask & __GFP_NORETRY)) {//若是內核可能執行影響VFS層的調用而又沒有設置GFP_NORETRY,那麼調用OOM killerif (!try_set_zone_oom(zonelist)) {schedule_timeout_uninterruptible(1);goto  
  21.  restart;}/* * Go through the zonelist yet one more time, keep * very high watermark here, this is only to catch * a parallel oom killing, we must fail if we're still * under heavy pressure. */page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, order,zonelist,  
  22.  ALLOC_WMARK_HIGH|ALLOC_CPUSET);if (page) {clear_zonelist_oom(zonelist);goto got_pg;}/* The OOM killer will not help higher order allocs so fail */if (order > PAGE_ALLOC_COSTLY_ORDER) {// 殺死一個進程未必當即出現多餘2^PAGE_ALLOC_CODTLY_ORDER頁的連續內存區,所以若是當前要分配如此大的內存區,那麼內核會饒恕所 選擇的進程,不執行殺死進程的任務,而是認可失敗並跳轉到nopageclear_zonelist_oom(zonelist);goto  
  23.  nopage;}out_of_memory(zonelist, gfp_mask, order);// 選擇一個內核認爲犯有分配過多內存「罪行」的進程,並殺死該進程。這有很大概率騰出較多的空閒頁,而後跳轉到標號restart,重試分配內存的操做 clear_zonelist_oom(zonelist);goto restart;}/* * Don't let big-order allocations loop unless the caller explicitly * requests that. Wait  
  24.  for some write requests to complete then retry. * * In this implementation, __GFP_REPEAT means __GFP_NOFAIL for order * <= 3, but that may not be true in other implementations. *///若是設置了__GFP_NORETRY,或內核不容許可能影響VFS層的操做do_retry = 0;if (!(gfp_mask & __GFP_NORETRY))  
  25.  {// 沒有設置__GFP_NORETRYif ((order <= PAGE_ALLOC_COSTLY_ORDER) ||(gfp_mask & amp; __GFP_REPEAT))//若是分配長度小於2^PAGE_ALLOC_COSTLY_ORDER或設置了__GFP_REPEAT,則 內核進入無限循環do_retry = 1;if (gfp_mask & __GFP_NOFAIL)//若是設置了不容許分配失敗,內核也會 進入無限循環do_retry = 1;}if (do_retry) {congestion_wait(WRITE,  
  26.  HZ/50);goto rebalance;}nopage:if (!(gfp_mask & __GFP_NOWARN) && printk_ratelimit()) {printk(KERN_WARNING "%s: page allocation failure."" order:%d, mode:0x%x\n",p->comm, order, gfp_mask);dump_stack();show_mem();}got_pg:return page;}  
  27. <pre></pre>  
  28. <p><span style="font-size:18px">get_page_from_freelist源代碼的詳細分析以下:</span></p>  
  29. <p></p>  
  30. <pre name="code" class="cpp">static struct page *  
  31. get_page_from_freelist(gfp_t gfp_mask, unsigned int order,  
  32.         struct zonelist *zonelist, int alloc_flags)  
  33. {  
  34.     struct zone **z;//管理區結構體  
  35.     struct page *page = NULL;  
  36.     int classzone_idx = zone_idx(zonelist->zones[0]);//#define zone_idx(zone)        ((zone) - (zone)->zone_pgdat->node_zones) 獲取管理區的編號  
  37.     struct zone *zone;  
  38.     nodemask_t *allowednodes = NULL;/* zonelist_cache approximation */  
  39.     int zlc_active = 0;     /* set if using zonelist_cache */  
  40.     int did_zlc_setup = 0;      /* just call zlc_setup() one time */  
  41.     enum zone_type highest_zoneidx = -1; /* Gets set for policy zonelists */  
  42.   
  43. zonelist_scan:  
  44.     /* 
  45.      * Scan zonelist, looking for a zone with enough free. 
  46.      * See also cpuset_zone_allowed() comment in kernel/cpuset.c. 
  47.      */  
  48.     z = zonelist->zones;//讓z指向第一個管理區  
  49.     //<span style="word- wrap: break-word; text-indent: 28px; background- color: rgb(245, 247, 248); " lang="EN-US"><span style="word- wrap: break-word; "> </span></span><span style="word- wrap: break-word; text-indent: 28px; background- color: rgb(245, 247, 248); ">在容許的節點中,遍歷知足要求的管理區</span>  
  50.     do {  
  51.         /* 
  52.          * In NUMA, this could be a policy zonelist which contains 
  53.          * zones that may not be allowed by the current gfp_mask. 
  54.          * Check the zone is allowed by the current flags 
  55.          */  
  56.         if (unlikely(alloc_should_filter_zonelist(zonelist))) {//根據zonelist->zlcache_ptr來肯定是否須要過濾掉此內存區鏈表,關於過濾的條件還不是很清楚,請指教  
  57.             if (highest_zoneidx == -1)  
  58.                 highest_zoneidx = gfp_zone(gfp_mask);//gfp_zone用於指定分配內存的內存域  
  59.             if (zone_idx(*z) > highest_zoneidx)//首先考慮利用上面指定的內存域,對於一些分配代價高於指定內存域的內存域先不考慮  
  60.                 continue;  
  61.         }  
  62.   
  63.         if (NUMA_BUILD && zlc_active &&//<span style="text-indent: 28px; background-color: rgb(245, 247, 248); ">是第一遍分配,在其餘管理區中分配頁面時須要考慮其頁面是否充足</span>  
  64.             !zlc_zone_worth_trying(zonelist, z, allowednodes))//<span style="text-indent: 28px; background-color: rgb(245, 247, 248); ">該管理區頁面不是很充足,考慮下一個管理區</span>  
  65.                 continue;  
  66.         zone = *z;  
  67.         if ((alloc_flags & ALLOC_CPUSET) &&  
  68.             !cpuset_zone_allowed_softwall(zone, gfp_mask))//<span style="text-indent: 28px; background-color: rgb(245, 247, 248); ">當前分配標誌不容許在該管理區中分配頁面</span>  
  69.                 goto try_next_zone;  
  70.   
  71.         if (!(alloc_flags & ALLOC_NO_WATERMARKS)) {//<span style="text-indent: 28px; background-color: rgb(245, 247, 248); ">分配時須要考慮watermark</span>  
  72.             unsigned long mark;//<span style="text-indent: 28px; background-color: rgb(245, 247, 248); ">根據分配標誌,肯定使用哪個watermark</span>  
  73.             if (alloc_flags & ALLOC_WMARK_MIN)  
  74.                 mark = zone->pages_min;  
  75.             else if (alloc_flags & ALLOC_WMARK_LOW)  
  76.                 mark = zone->pages_low;  
  77.             else  
  78.                 mark = zone->pages_high;  
  79.             if (!zone_watermark_ok(zone, order, mark,  
  80.                     classzone_idx, alloc_flags)) {//<span style="text-indent: 28px; background-color: rgb(245, 247, 248); ">該管理區的可用內存不能夠知足本次分配的要求</span>  
  81.                 if (!zone_reclaim_mode ||//但不知足分配要求時,若是此內存域不能回收內存或者是回收不到可用內存時,就會跳轉到this_zone_full  
  82.                     !zone_reclaim(zone, gfp_mask, order))  
  83.                     goto this_zone_full;  
  84.             }  
  85.         }  
  86.   
  87.         page = buffered_rmqueue(zonelist, zone, order, gfp_mask);//<span style="text-indent: 28px; background-color: rgb(245, 247, 248); ">調用夥伴系統的分配函數</span>  
  88.         if (page)//<span style="word- wrap: break-word; text-indent: 28px; background- color: rgb(245, 247, 248); " lang="EN-US"><span style="word- wrap: break-word; "> </span></span><span style="word- wrap: break-word; text-indent: 28px; background- color: rgb(245, 247, 248); ">從夥伴系統分配成功,退出</span>  
  89.             break;  
  90. this_zone_full:  
  91.         if (NUMA_BUILD)  
  92.             zlc_mark_zone_full(zonelist, z);//<span style="text-indent: 28px; background-color: rgb(245, 247, 248); ">標記該管理區空間不足,下次分配時將略過本管理區,避免浪費太多時間</span>  
  93. try_next_zone:  
  94.         if (NUMA_BUILD && !did_zlc_setup) {//<span style="text-indent: 28px; background-color: rgb(245, 247, 248); ">當前管理區內存不足,須要加大在其餘區中的分配力度</span>  
  95.             /* we do zlc_setup after the first zone is tried */  
  96.             allowednodes = zlc_setup(zonelist, alloc_flags);  
  97.             zlc_active = 1;  
  98.             did_zlc_setup = 1;  
  99.         }  
  100.     } while (*(++z) != NULL);  
  101.   
  102.     if (unlikely(NUMA_BUILD && page == NULL && zlc_active)) {//<span style="word- wrap: break-word; text-indent: 28px; background- color: rgb(245, 247, 248); " lang="EN-US"><span style="word- wrap: break-word; "> </span></span><span style="word- wrap: break-word; text-indent: 28px; background- color: rgb(245, 247, 248); ">第一遍分配不成功,則取 消</span><span style="word-wrap: break-word; text- indent: 28px; background-color: rgb(245, 247, 248); " lang="EN-US">& lt;span style="word-wrap: break-word; ">zlc_active</span>< /span><span style="word-wrap: break-word; text- indent: 28px; background-color: rgb(245, 247, 248); ">,這樣會盡可能從其餘節點中分配內 存</span>  
  103.         /* Disable zlc cache for second zonelist scan */  
  104.         zlc_active = 0;  
  105.         goto zonelist_scan;  
  106.     }  
  107.     return page;  
  108. }  
  109. </pre>  
  110. <p><span style="font-size:18px; color:#ff0000">關於上面一段代碼中zlc_active的做用不明白,還望理解的人指點一下。</span></p>  
  111. <p></p>  
  112. <pre name="code" class="cpp">struct zonelist {  
  113.     struct zonelist_cache *zlcache_ptr;          // NULL or &zlcache  
  114.     struct zone *zones[MAX_ZONES_PER_ZONELIST + 1];      // NULL delimited  
  115. #ifdef CONFIG_NUMA  
  116.     struct zonelist_cache zlcache;               // optional ...  
  117. #endif  
  118. };</pre><br>  
  119. <pre name="code" class="cpp">struct zonelist_cache {  
  120.     unsigned short z_to_n[MAX_ZONES_PER_ZONELIST];      /* zone->nid */  
  121.     DECLARE_BITMAP(fullzones, MAX_ZONES_PER_ZONELIST);  /* zone full? */  
  122.     unsigned long last_full_zap;        /* when last zap'd (jiffies) */  
  123. };</pre><br>  
  124. <span style="font-size:18px">zone_watermark_ok源代碼詳細分析以下:</span><br>  
  125. <p></p>  
  126. <p></p>  
  127. <pre name="code" class="cpp">int zone_watermark_ok(struct zone *z, int order, unsigned long mark,  
  128.               int classzone_idx, int alloc_flags)  
  129. {  
  130.     /* free_pages my go negative - that's OK */  
  131.     long min = mark;  
  132.     long free_pages = zone_page_state(z, NR_FREE_PAGES) - (1 << order) + 1;//zone_page_state用來訪問每一個內存域的統計量,在此處,獲得的是空閒頁的數目  
  133.     int o;  
  134.   
  135.     if (alloc_flags & ALLOC_HIGH)//設置了ALLOC_HIGH以後,將最小值標記減小一半  
  136.         min -= min / 2;  
  137.     if (alloc_flags & ALLOC_HARDER)//設置了ALLOC_HARDER以後,將最小值標記減小1/4  
  138.         min -= min / 4;  
  139.   
  140.     if (free_pages <= min + z->lowmem_reserve[classzone_idx])//檢查空閒頁的數目是否小於最小值與lowmem_reserve中制定的緊急分配值之和,若是小於則不進行內存分配  
  141.         return 0;  
  142.     for (o = 0; o < order; o++) {//若是不小於,則代碼遍歷全部小於當前階的分配階  
  143.         /* At the next order, this order's pages become unavailable */  
  144.         free_pages -= z->free_area[o].nr_free << o;//從free_pages減去當前分配階的全部空閒頁  
  145.   
  146.         /* Require fewer higher order pages to be free */  
  147.         min >>= 1;// 每升高一階,所需空閒頁的最小值減半,<span style="background- color: rgb(245, 247, 248); ">由於階數越高,每個塊中包含的頁面就越多。咱們假設初始水線是2^n,那麼對階數0 來講,min的值就應當是2^n,對階數爲1來講,min的值就應當除以2變爲2^(n-1),由於對於階數1來講,每一個塊包含的頁面數爲 2</span>  
  148.   
  149.         if (free_pages <= min)//若是內核遍歷全部的低端內存域以後,發現內存不足,則不進行內存分配  
  150.             return 0;  
  151.     }  
  152.     return 1;  
  153. }  
  154. </pre><span style="font-size:18px">buffered_rmqueue源代碼詳細分析以下:</span>  
  155. <p></p>  
  156. <p></p>  
  157. <pre name="code" class="cpp">static struct page *buffered_rmqueue(struct zonelist *zonelist,  
  158.             struct zone *zone, int order, gfp_t gfp_flags)  
  159. {  
  160.     unsigned long flags;  
  161.     struct page *page;  
  162.     int cold = !!(gfp_flags & __GFP_COLD);//若是分配參數指定了__GFP_COLD標誌,則設置cold標誌,兩次取反操做確保cold是0或者1,why?請指教  
  163.     int cpu;  
  164.     int migratetype = allocflags_to_migratetype(gfp_flags);//根據gfp_flags得到遷移類型  
  165.   
  166. again:  
  167.     cpu  = get_cpu();//獲取本CPU  
  168.     if (likely(order == 0)) {//分配單頁,須要管理每CPU頁面緩存  
  169.         struct per_cpu_pages *pcp;  
  170.   
  171.         pcp = &zone_pcp(zone, cpu)->pcp[cold];//取得本CPU的頁面緩存對象  
  172.         local_irq_save(flags);//這裏須要關中斷,由於內存回收過程可能發送核間中斷,強制每一個核從每CPU緩存中釋放頁面。並且中斷處理函數也會分配單頁。  
  173.         if (!pcp->count) {//緩存爲空,須要擴大緩存的大小  
  174.             pcp->count = rmqueue_bulk(zone, 0,  
  175.                     pcp->batch, &pcp->list, migratetype);//從夥伴系統中摘除一批頁面到緩存中,補充的頁面個數由每CPU緩存的batch字段指定  
  176.             if (unlikely(!pcp->count))//若是緩存仍然爲空,那麼說明夥伴系統中頁面也沒有了,分配失敗  
  177.                 goto failed;  
  178.         }  
  179.   
  180.         /* Find a page of the appropriate migrate type */  
  181.         list_for_each_entry(page, &pcp->list, lru)//遍歷每CPU緩存中的全部頁,檢查是否有指定類型的遷移類型的頁可用  
  182.             if (page_private(page) == migratetype)  
  183.                 break;  
  184.   
  185.         /* Allocate more to the pcp list if necessary */  
  186.         if (unlikely(&page->lru == &pcp->list)) {  
  187.             pcp->count += rmqueue_bulk(zone, 0,  
  188.                     pcp->batch, &pcp->list, migratetype);  
  189.             page = list_entry(pcp->list.next, struct page, lru);  
  190.         }  
  191.   
  192.         list_del(&page->lru);//將頁面從每CPU緩存鏈表中取出,並將每CPU緩存計數減1  
  193.         pcp->count--;  
  194.     } else {  
  195.         spin_lock_irqsave(&zone->lock, flags);  
  196.         page = __rmqueue(zone, order, migratetype);  
  197.         spin_unlock(&zone->lock);  
  198.         if (!page)  
  199.             goto failed;  
  200.     }  
  201.   
  202.     __count_zone_vm_events(PGALLOC, zone, 1 << order);  
  203.     zone_statistics(zonelist, zone);  
  204.     local_irq_restore(flags);  
  205.     put_cpu();  
  206.   
  207.     VM_BUG_ON(bad_range(zone, page));  
  208.     if (prep_new_page(page, order, gfp_flags))  
  209.         goto again;  
  210.     return page;  
  211.   
  212. failed:  
  213.     local_irq_restore(flags);  
  214.     put_cpu();  
  215.     return NULL;  
  216. }</pre><span style="color:rgb(255,0,0); font-family:Arial; font-size:18px; line-height:26px"> 我也知道有不少的細節都沒有分析到位,可是我也沒有辦法,曾經想着把裏面涉及到的每個函數都分析到位,可是那樣的話本身至關的痛苦,由於那樣的結果就是 不少天都沒有辦法前進一點,會讓人至關的有挫敗感,最後只能選擇大概先都過一遍,由於本身是一個內核的初學者,而內核先後的關聯又很大,也只能先過一遍, 到後面我會從新回來看我寫得博客,能增進一些分析就增進一些分析。若是您認爲上面確實有很重要的地方我沒有分析到,但願您指 點。</span><br>  
  217. <br>  
  218. <br>  
  219. <p></p>  
  220. <br>  
  221. <p></p>  
  222. <p><br>  
  223. </p>  
  224. <br>  
  225. <br>  
  226. <br>  
  227. <br>  
  228. <br>  
  229. <br>  
  230. <br>  
  231. <br>  
  232. <br>  
  233. <p></p> 
相關文章
相關標籤/搜索