Linux内存管理之伙伴系统(内存分配)

/*   * Really, prep_compound_page() should be called from __rmqueue_bulk().  But   * we cheat by calling it from here, in the order > 0 path.  Saves a branch   * or two.   */    /*先考虑从pcp中分配空间,当order大于0时再考虑从伙伴系统中分配*/   static inline   struct page *buffered_rmqueue(struct zone *preferred_zone,               struct zone *zone, int order, gfp_t gfp_flags,               int migratetype)   {       unsigned long flags;       struct page *page;       int cold = !!(gfp_flags & __GFP_COLD);/*如果分配参数指定了__GFP_COLD标志,则设置cold标志*/       int cpu;      again:       cpu  = get_cpu();       if (likely(order == 0)) {/*分配一个页面时,使用pcp*/           struct per_cpu_pages *pcp;           struct list_head *list;           /*找到zone对应的pcp*/           pcp = &zone_pcp(zone, cpu)->pcp;           list = &pcp->lists[migratetype];/*pcp中对应类型的list*/                      /* 这里需要关中断,因为内存回收过程可能发送核间中断,强制每个核从每CPU          缓存中释放页面。而且中断处理函数也会分配单页。 */           local_irq_save(flags);           if (list_empty(list)) {/*如果pcp中没有页面,需要补充*/               /*从伙伴系统中获得batch个页面              batch为一次分配的页面数*/               pcp->count += rmqueue_bulk(zone, 0,                       pcp->batch, list,                       migratetype, cold);               /*如果链表仍然为空,申请失败返回*/               if (unlikely(list_empty(list)))                   goto failed;           }           /* 如果分配的页面不需要考虑硬件缓存(注意不是每CPU页面缓存)          ,则取出链表的最后一个节点返回给上层*/           if (cold)               page = list_entry(list->prev, struct page, lru);           else/* 如果要考虑硬件缓存,则取出链表的第一个页面,这个页面是最近刚释放到每CPU              缓存的,缓存热度更高 */               page = list_entry(list->next, struct page, lru);              list_del(&page->lru);/*从pcp中脱离*/           pcp->count--;/*pcp计数减一*/       }        else {/*当order为大于1时,不从pcp中分配,直接考虑从伙伴系统中分配*/           if (unlikely(gfp_flags & __GFP_NOFAIL)) {               /*               * __GFP_NOFAIL is not to be used in new code.               *               * All __GFP_NOFAIL callers should be fixed so that they               * properly detect and handle allocation failures.               *               * We most definitely don't want callers attempting to               * allocate greater than order-1 page units with               * __GFP_NOFAIL.               */               WARN_ON_ONCE(order > 1);           }           /* 关中断,并获得管理区的锁*/           spin_lock_irqsave(&zone->lock, flags);           /*从伙伴系统中相应类型的相应链表中分配空间*/           page = __rmqueue(zone, order, migratetype);           /* 已经分配了1 << order个页面,这里进行管理区空闲页面统计计数*/           __mod_zone_page_state(zone, NR_FREE_PAGES, -(1 << order));           spin_unlock(&zone->lock);/* 这里仅仅打开自旋锁,待后面统计计数设置完毕后再开中断*/           if (!page)               goto failed;       }       /*事件统计计数,调试*/       __count_zone_vm_events(PGALLOC, zone, 1 << order);       zone_statistics(preferred_zone, zone);       local_irq_restore(flags);/*恢复中断*/       put_cpu();          VM_BUG_ON(bad_range(zone, page));               /* 这里进行安全性检查,并进行一些善后工作。        如果页面标志破坏,返回的页面出现了问题,则返回试图分配其他页面*/       if (prep_new_page(page, order, gfp_flags))           goto again;       return page;      failed:       local_irq_restore(flags);       put_cpu();       return NULL;   }  

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/22862.html