/* * 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; }
Linux内存管理之伙伴系统(内存分配)
内容版权声明:除非注明,否则皆为本站原创文章。