Linux伙伴系统(四)--释放页

来源:互联网 发布:2016淘宝美工有前途吗 编辑:程序博客网 时间:2024/05/18 00:17

水平有限,描述不当之处还请之处,转载请注明出处http://blog.csdn.net/vanbreaker/article/details/7624628  

     

      Linux内存释放函数之间的调用关系如下图所示

         

      可以看到,落脚点是__free_pages()这个函数,它执行的工作的流程图如下图所示

        

 

 

 下面是该函数的具体代码

[cpp] view plaincopy
  1. void __free_pages(struct page *page, unsigned int order)  
  2. {  
  3.     if (put_page_testzero(page)) {/*判断页没有被使用*/  
  4.         trace_mm_page_free_direct(page, order);  
  5.         if (order == 0)/*单页则释放到每CPU页框高速缓存中*/  
  6.             free_hot_page(page);  
  7.         else           /*多页则释放到伙伴系统*/  
  8.             __free_pages_ok(page, order);  
  9.     }  
  10. }  

 

  • 首先该函数要通过页描述符的引用计数来判断该页是否是空闲的
  • 确定页是空闲的后,再判断要释放多少个页面,如果是单个页面则将该页作为热页释放到pcp中,如果是多页则释

          放到伙伴系统中

     

    [cpp] view plaincopy
    1. void free_hot_page(struct page *page)  
    2. {  
    3.     trace_mm_page_free_direct(page, 0);  
    4.     free_hot_cold_page(page, 0);  
    5. }  


    free_hot_page是free_hot_cold_page的封装

    [cpp] view plaincopy
    1. static void free_hot_cold_page(struct page *page, int cold)  
    2. {  
    3.     struct zone *zone = page_zone(page);  
    4.     struct per_cpu_pages *pcp;  
    5.     unsigned long flags;  
    6.     int migratetype;  
    7.     int wasMlocked = __TestClearPageMlocked(page);  
    8.   
    9.     kmemcheck_free_shadow(page, 0);  
    10.   
    11.     if (PageAnon(page))  
    12.         page->mapping = NULL;  
    13.     if (free_pages_check(page))  
    14.         return;  
    15.   
    16.     if (!PageHighMem(page)) {  
    17.         debug_check_no_locks_freed(page_address(page), PAGE_SIZE);  
    18.         debug_check_no_obj_freed(page_address(page), PAGE_SIZE);  
    19.     }  
    20.     arch_free_page(page, 0);  
    21.     kernel_map_pages(page, 1, 0);  
    22.   
    23.     /*获取对应的pcp结构*/  
    24.     pcp = &zone_pcp(zone, get_cpu())->pcp;  
    25.     /*获取迁移类型*/  
    26.     migratetype = get_pageblock_migratetype(page);  
    27.     set_page_private(page, migratetype);  
    28.     local_irq_save(flags);  
    29.     if (unlikely(wasMlocked))  
    30.         free_page_mlock(page);  
    31.     __count_vm_event(PGFREE);  
    32.   
    33.     /* 
    34.      * We only track unmovable, reclaimable and movable on pcp lists. 
    35.      * Free ISOLATE pages back to the allocator because they are being 
    36.      * offlined but treat RESERVE as movable pages so we can get those 
    37.      * areas back if necessary. Otherwise, we may have to free 
    38.      * excessively into the page allocator 
    39.      */  
    40.      /*只有不可移动页,可回收页和可移动页才能放到每CPU页框高速缓存中,如果 
    41.         迁移类型不属于这个范围,则要将该页释放回伙伴系统*/  
    42.     if (migratetype >= MIGRATE_PCPTYPES) {  
    43.         if (unlikely(migratetype == MIGRATE_ISOLATE)) {  
    44.             free_one_page(zone, page, 0, migratetype);  
    45.             goto out;  
    46.         }  
    47.         migratetype = MIGRATE_MOVABLE;  
    48.     }  
    49.   
    50.     if (cold)/*冷页插入表尾*/  
    51.         list_add_tail(&page->lru, &pcp->lists[migratetype]);  
    52.     else     /*热页插入表头*/  
    53.         list_add(&page->lru, &pcp->lists[migratetype]);  
    54.     pcp->count++;  
    55.     /*如果pcp中的页面数超过了high,则释放2^batch个单页给伙伴系统*/  
    56.     if (pcp->count >= pcp->high) {  
    57.         free_pcppages_bulk(zone, pcp->batch, pcp);  
    58.         pcp->count -= pcp->batch;  
    59.     }  
    60.   
    61. out:  
    62.     local_irq_restore(flags);  
    63.     put_cpu();  
    64. }  


     

    伙伴系统的分支__free_pages_ok()先对释放的页做了些检查,然后具体的释放通过调用free_one_page()-->__free_one_page()来完成

    [cpp] view plaincopy
    1. static inline void __free_one_page(struct page *page,  
    2.         struct zone *zone, unsigned int order,  
    3.         int migratetype)  
    4. {  
    5.     unsigned long page_idx;  
    6.   
    7.     if (unlikely(PageCompound(page)))  
    8.         if (unlikely(destroy_compound_page(page, order)))  
    9.             return;  
    10.   
    11.     VM_BUG_ON(migratetype == -1);  
    12.   
    13.     /*得到页框在所处最大块中的偏移*/  
    14.     page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1);  
    15.   
    16.     VM_BUG_ON(page_idx & ((1 << order) - 1));  
    17.     VM_BUG_ON(bad_range(zone, page));  
    18.   
    19.     /*只要阶数小于MAX_ORDER-1就有合并的机会*/  
    20.     while (order < MAX_ORDER-1) {  
    21.         unsigned long combined_idx;  
    22.         struct page *buddy;  
    23.   
    24.         /*找到page所处块对应的伙伴块*/  
    25.         buddy = __page_find_buddy(page, page_idx, order);  
    26.         /*如果伙伴块不是空闲的则不执行下面的合并操作*/  
    27.         if (!page_is_buddy(page, buddy, order))  
    28.             break;  
    29.   
    30.         /* Our buddy is free, merge with it and move up one order. */  
    31.         list_del(&buddy->lru);/*将伙伴块从块链表中删除*/  
    32.         zone->free_area[order].nr_free--;  
    33.         rmv_page_order(buddy);  
    34.         /*计算出合并块的起始页框的偏移*/  
    35.         combined_idx = __find_combined_index(page_idx, order);  
    36.         /*得到合并块的起始页描述符*/  
    37.         page = page + (combined_idx - page_idx);  
    38.         page_idx = combined_idx;/*修改块的起始页偏移*/  
    39.         order++;/*阶数加1表明合并完成*/  
    40.     }  
    41.     /*重新设置块的阶数*/  
    42.     set_page_order(page, order);  
    43.     /*将新块添加到对应的链表中*/  
    44.     list_add(&page->lru,  
    45.         &zone->free_area[order].free_list[migratetype]);  
    46.     zone->free_area[order].nr_free++;  
    47. }  


     

    这里面涉及到两个辅助函数,_page_find_buddy()用来找到是释放块的伙伴,如果找到了一个空闲的伙伴块要通过_find_combined_index()用来定位合并块的起始页框,因为一个块的伙伴块有可能在该块的前面,也有可能在该块的后面,这两个函数的实现非常简洁巧妙,全是通过位操作来实现的

    [cpp] view plaincopy
    1. static inline struct page *  
    2. __page_find_buddy(struct page *page, unsigned long page_idx, unsigned int order)  
    3. {  
    4.     unsigned long buddy_idx = page_idx ^ (1 << order);  
    5.   
    6.     return page + (buddy_idx - page_idx);  
    7. }  


     

    [cpp] view plaincopy
    1. <span style="font-size:12px;">static inline unsigned long  
    2. __find_combined_index(unsigned long page_idx, unsigned int order)  
    3. {  
    4.     return (page_idx & ~(1 << order));  
    5. }</span>  


    我们可以通过一个简单的情形来模拟一下这个过程,假设现在有一个将要释放的页,它的order为0,page_idx为10

    则先计算它的伙伴 10^(1<<0) = 11,然后计算合并后的起始页偏移为10&~(1<<0) = 10,现在就得到了一个order为1的块,起始页偏移为10,它的伙伴为10^(1<<1)=8,合并后的起始页偏移为10&~(1<<1)=8,如此推导下去,我们可以通过下图和下表更清晰地分析这个过程

     

     

    其中pi代表page_idx, ci代表combined_idx

  • 0 0
    原创粉丝点击