分享

Linux内存管理伙伴算法释放页

 mediatv 2013-01-17
  
 

__free_pages源代码详细分析如下:

  1. fastcall void __free_pages(struct page *page, unsigned int order)  
  2. {  
  3.     if (put_page_testzero(page)) {//一般来说,只有当页面的引用计数变为0时,才会释放该页,此处进行页面引用计数的判断   
  4.         if (order == 0)//如果是释放单个页面,则释放到管理区的每CPU页面缓存中,以加快单页分配速度   
  5.             free_hot_page(page);  
  6.         else  
  7.             __free_pages_ok(page, order);//否则直接释放到伙伴系统中    
  8.     }  
  9. }  
  10. void fastcall free_hot_page(struct page *page)  
  11. {  
  12.     free_hot_cold_page(page, 0);  
  13. }  
  14. free_hot_cold_page源代码详细分析如下:  
  15.   
  16. static void fastcall free_hot_cold_page(struct page *page, int cold)  
  17. {  
  18.     struct zone *zone = page_zone(page);//找到页面所属的管理区   
  19.     struct per_cpu_pages *pcp;//为等下访问冷热区做准备,pcp是一个中间变量   
  20.     unsigned long flags;  
  21.   
  22.     if (PageAnon(page))//说明page->mapping的第0位是1,说明该页是匿名页   
  23.         page->mapping = NULL;//则使page->mapping指向NULL   
  24.     if (free_pages_check(page))//这个函数就是检测page的flag和mapping,_count,_mapcount这几个成员,如果有误,就会进行相应的处理   
  25.         return;  
  26.   
  27.     if (!PageHighMem(page))//如果该内存页不在高端内存域   
  28.         debug_check_no_locks_freed(page_address(page), PAGE_SIZE);//则调试相关代码   
  29.     arch_free_page(page, 0);//只有配置了HAVE_ARCH_FREE_PAGE才会起作用   
  30.     kernel_map_pages(page, 1, 0);  
  31.   
  32.     pcp = &zone_pcp(zone, get_cpu())->pcp[cold];//获得本页区的当前cpu,然后找到对应的热区的高速缓存内存,用pcp指针指向它   
  33.     local_irq_save(flags);//禁止irq中断   
  34.     __count_vm_event(PGFREE);//统计本CPU上的页面释放次数   
  35.     list_add(&page->lru, &pcp->list);//把page的lru挂在pcp->list上,挂在pcp->list前面   
  36.     set_page_private(page, get_pageblock_migratetype(page));//获取页面的迁移类型,并将其保存在page->private中   
  37.     pcp->count++;//更新当前CPU页面缓存计数   
  38.     if (pcp->count >= pcp->high) {//页面缓存中保留的页面太多,超过上限了   
  39.         free_pages_bulk(zone, pcp->batch, &pcp->list, 0);//将页面缓存中的页面释放一部分到伙伴系统中   
  40.         pcp->count -= pcp->batch;//更新当前CPU中页面缓存计数   
  41.     }  
  42.     local_irq_restore(flags);//恢复中断   
  43.     put_cpu();//preempt_enable()是抢占使能   
  44. }  
  45.   
  46. free_pages_bulk源代码详细分析如下:  
  47. static void free_pages_bulk(struct zone *zone, int count,  
  48.                     struct list_head *list, int order)  
  49. {  
  50.     spin_lock(&zone->lock);//上锁   
  51.     zone_clear_flag(zone, ZONE_ALL_UNRECLAIMABLE);//在释放页之前,先将内存区中页不可回收的标志清除   
  52.     zone->pages_scanned = 0;//在上一次成功换出页之后,已扫描页计数器   
  53.     while (count--) {//循环,直到把count个页面全部释放   
  54.         struct page *page;  
  55.   
  56.         VM_BUG_ON(list_empty(list));  
  57.         page = list_entry(list->prev, struct page, lru);//通过pcp->list找到第一个lru,再通过lru成员找到对应的struct page   
  58.         /* have to delete it as __free_one_page list manipulates */  
  59.         list_del(&page->lru);//lru取出后,pcp->list链表就不再指向它了,就要从链表里面移除   
  60.         __free_one_page(page, zone, order);//这是释放函数的主体,下面会详细讨论   
  61.     }  
  62.     spin_unlock(&zone->lock);//解锁   
  63. }  
  64.   
  65. _free_pages_ok源代码详细分析如下:  
  66. static void __free_pages_ok(struct page *page, unsigned int order)  
  67. {  
  68.     unsigned long flags;  
  69.     int i;  
  70.     int reserved = 0;  
  71.   
  72.     for (i = 0 ; i < (1 << order) ; ++i)  
  73.         reserved += free_pages_check(page + i);//在释放页之前,还得先用free_pages_check函数检查页是否有错误,如果有错误就要进行相应的处理,如果没有错误,还需要记录所有页面中需要保留的页面数   
  74.     if (reserved)//如果在需要释放的一段内存中有需要保留的内存页,那么该段内存就不能释放   
  75.         return;  
  76.   
  77.     if (!PageHighMem(page))//判断页是否在高端内存域中   
  78.         debug_check_no_locks_freed(page_address(page),PAGE_SIZE<<order);//调试用的相关代码,还不理解,还望指点!   
  79.     arch_free_page(page, order);//只有配置了HAVE_ARCH_FREE_PAGE才会起作用,也不理解,还望指点   
  80.     kernel_map_pages(page, 1 << order, 0);  
  81.   
  82.     local_irq_save(flags);//禁止中断   
  83.     __count_vm_events(PGFREE, 1 << order);//统计本CPU上释放的页数   
  84.     free_one_page(page_zone(page), page, order);//释放函数的主体__free_one_page的封装   
  85.     local_irq_restore(flags);//恢复中断   
  86. }  
  87.   
  88. static void free_one_page(struct zone *zone, struct page *page, int order)  
  89. {  
  90.     spin_lock(&zone->lock);//获得管理区得自旋锁   
  91.     zone_clear_flag(zone, ZONE_ALL_UNRECLAIMABLE);//在释放之前,先将内存区中页的不可回收标志清除   
  92.     zone->pages_scanned = 0;//初始化在上一次成功换出页之后,已扫描页的数目。目前正在释放内存,将此清0,待回收过程随后回收时重新计数   
  93.     __free_one_page(page, zone, order);//释放函数的主体   
  94.     spin_unlock(&zone->lock);//解锁,有关锁的机制,自己还没有看到,后面的Blog会详细分析   
  95. }  
  96.   
  97. __free_one_page源代码详细分析如下:  
  98. static inline void __free_one_page(struct page *page,  
  99.         struct zone *zone, unsigned int order)  
  100. {  
  101.     unsigned long page_idx;  
  102.     int order_size = 1 << order;  
  103.     int migratetype = get_pageblock_migratetype(page);  
  104.   
  105.     if (unlikely(PageCompound(page)))//要释放的页是巨页的一部分   
  106.         destroy_compound_page(page, order);//解决巨页标志,如果巨页标志有问题,则退出   
  107.   
  108.     page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1);//获取该页得页帧号,并保证该值在一定的范围之内   
  109.   
  110.     VM_BUG_ON(page_idx & (order_size - 1));//如果被释放的页不是所释放阶的第一个页,则说明参数有误   
  111.     VM_BUG_ON(bad_range(zone, page));//检查页面是否处于zone之中   
  112.   
  113.     __mod_zone_page_state(zone, NR_FREE_PAGES, order_size);  
  114.     while (order < MAX_ORDER-1) {//释放页以后,当前页面可能与前后的空闲页组成更大的空闲页面,直到放到最大阶的伙伴系统中   
  115.         unsigned long combined_idx;  
  116.         struct page *buddy;  
  117.   
  118.         buddy = __page_find_buddy(page, page_idx, order);//找到与当前页属于同一个阶的伙伴系统页面的索引   
  119.         if (!page_is_buddy(page, buddy, order))//判断预期的伙伴页面是否与当前页处于同一个管理区,并且是否处于空闲状态。如果不是,则不能与该页合并为大的伙伴,退出   
  120.             break;      /* Move the buddy up one level. */  
  121.   
  122.         list_del(&buddy->lru);//如果能够合并,则将伙伴页从伙伴系统中摘除   
  123.         zone->free_area[order].nr_free--;//同时减少当前阶中的空闲页计数   
  124.         rmv_page_order(buddy);//清除伙伴页的伙伴标志,因为该页会被合并   
  125.         combined_idx = __find_combined_index(page_idx, order);//计算合并内存块的索引   
  126.         page = page + (combined_idx - page_idx);//得到新的页面起始地址   
  127.         page_idx = combined_idx;//为下一轮循环做准备   
  128.         order++;//为下一轮循环做准备   
  129.     }  
  130.     set_page_order(page, order);//设置伙伴页中第一个空闲页的阶   
  131.     list_add(&page->lru,  
  132.         &zone->free_area[order].free_list[migratetype]);//将当前页面合并到空闲链表的最后,尽量避免将它分配出去   
  133.     zone->free_area[order].nr_free++;  
  134. }  
  135.   
 

 
 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多