unsigned long flags; int migratetype; int wasMlocked = __TestClearPageMlocked(page); if (!free_pages_prepare(page, 0)) return; 获取页的迁移类型 migratetype = get_pageblock_migratetype(page); 将页的迁移类型保存到page->private中 set_page_private(page, migratetype); local_irq_save(flags); if (unlikely(wasMlocked)) free_page_mlock(page); __count_vm_event(PGFREE); MIGRATE_PCPTYPES是每CPU缓存管理页帧类型的数目,如果某个页帧类型大于MIGRATE_PCPTYPES就将其挂到可移动列表中,如果迁移类型是MIGRATE_ISOLATE就直接将该页帧释放到伙伴系统中。 if (migratetype >= MIGRATE_PCPTYPES) { if (unlikely(migratetype == MIGRATE_ISOLATE)) { free_one_page(zone, page, 0, migratetype); goto out; } migratetype = MIGRATE_MOVABLE; } 取得内存域的每CPU管理结构 pcp = &this_cpu_ptr(zone->pageset)->pcp; Cold标示冷页或热页,如果是冷页就将页帧挂到对应迁移类型链表的末尾,如果是热页就将页帧挂到对应迁移类型链表的头部 if (cold) list_add_tail(&page->lru, &pcp->lists[migratetype]); else list_add(&page->lru, &pcp->lists[migratetype]); pcp->count++; 每CPU缓存有一个最大页帧的阀值,如果每CPU缓存中页帧数大于这个阀值,就将一批(pcp->batch)页帧释到伙伴系统中。 if (pcp->count >= pcp->high) { free_pcppages_bulk(zone, pcp->batch, pcp); pcp->count -= pcp->batch; } out: local_irq_restore(flags); } 【__free_pages--->free_hot_cold_page--->free_pcppages_bulk】 static void free_pcppages_bulk(struct zone *zone, int count,struct per_cpu_pages *pcp) { int migratetype = 0; int batch_free = 0; int to_free = count; spin_lock(&zone->lock); all_unreclaimable表示内存紧张程度,释放内存后将此标志清除 zone->all_unreclaimable = 0; pages_scanned最后一次内存紧张以来,页面回收过程已经扫描的页数。现在正在释放类存,将此值清零,待回收时重新计数 zone->pages_scanned = 0; 循环将一批每CPU缓存中页帧释放到伙伴系统中去 while (to_free) { struct page *page; struct list_head *list; do { 在每CPU管理结构中取出一个不为空的列表,batch_free++表示迁移类型越靠后的链表中释放的页越多。 batch_free++; if (++migratetype == MIGRATE_PCPTYPES) migratetype = 0; list = &pcp->lists[migratetype]; } while (list_empty(list)); if (batch_free == MIGRATE_PCPTYPES) batch_free = to_free; do { 将页帧从链表中取下,然后释放 page = list_entry(list->prev, struct page, lru); list_del(&page->lru); __free_one_page(page, zone, 0, page_private(page)); trace_mm_page_pcpu_drain(page, 0, page_private(page)); } while (--to_free && --batch_free && !list_empty(list)); } __mod_zone_page_state(zone, NR_FREE_PAGES, count); spin_unlock(&zone->lock); } 【__free_pages--->free_hot_cold_page--->free_pcppages_bulk--->__free_one_page】 mm/page_alloc.c static inline void __free_one_page(struct page *page, struct zone *zone, unsigned int order, int migratetype) { unsigned long page_idx; unsigned long combined_idx; unsigned long uninitialized_var(buddy_idx); struct page *buddy; 复合页的第一个页称作首页,其余所有各页称作尾页。组成复合页的所有页的page实例的private成员,包括首页在内,都指向首页。如果要释放的页是复合页就清除复合页的一些标志,让其变为普通页然后释放。 if (unlikely(PageCompound(page))) if (unlikely(destroy_compound_page(page, order))) return; VM_BUG_ON(migratetype == -1); 伙伴系统中,页帧的周期为2^MAX_ORDER,page_idx是周期内的偏移 page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1); VM_BUG_ON(page_idx & ((1 << order) - 1)); VM_BUG_ON(bad_range(zone, page)); 遍历order以上所有阶,将页帧依次向上合并 while (order < MAX_ORDER-1) { 函数__find_buddy_index就一行代码page_idx ^ (1 << order),将page_idx的1 << order位取反。 buddy_idx = __find_buddy_index(page_idx, order); 如果page_idx的1 << order位为1,buddy_idx 与page_idx相减的结果就是负的(1 << order);如果如果page_idx的1 << order位为0,相减的结果就为正的(1 << order)。Buddy是page的伙伴页。以当前页帧实例page为基准,向左或向右偏移(1 << order)。 buddy = page + (buddy_idx - page_idx); 函数page_is_buddy主要做四项检查: 1,伙伴页是否处于一个空洞中 2,伙伴页是否在伙伴系统中 3,page和它的伙伴页是否有相同的阶 4,page和它的伙伴页是否处于相同的内存域中 if (!page_is_buddy(page, buddy, order)) break;
if (page_is_guard(buddy)) { clear_page_guard_flag(buddy); set_page_private(page, 0); __mod_zone_page_state(zone, NR_FREE_PAGES, 1 << order); } else { 将伙伴页从列表中删除,因为它将和其它页合并,然后将首页挂到更高阶的列表中。 list_del(&buddy->lru); 递减该阶中的页计数 zone->free_area[order].nr_free--; 清除页的“在伙伴系统中”的标志,然后将页块的阶数page->private置为0 rmv_page_order(buddy); } 将page_idx的1 << order位清零 combined_idx = buddy_idx & page_idx; 求order+1阶页块的首页 page = page + (combined_idx - page_idx); page_idx = combined_idx; 更新page_idx order++;阶数递增 } 设置首页的阶数,将首页标记为伙伴系统页 set_page_order(page, order); 在order阶向order+1阶的页块合并中失败了,下面是判断如果下次order阶向order+1阶的页块合并能够成功的话,就将order阶页块链接到列表的末尾,避免下次被分配出去,以便以后合并为更高阶的页块。 if ((order < MAX_ORDER-2) && pfn_valid_within(page_to_pfn(buddy))) { struct page *higher_page, *higher_buddy; 求高一阶的页块首页 combined_idx = buddy_idx & page_idx; higher_page = page + (combined_idx - page_idx); 找出高一阶页块的伙伴页 buddy_idx = __find_buddy_index(combined_idx, order + 1); higher_buddy = page + (buddy_idx - combined_idx); 检查高一阶页是否可以合并,如果可以合并就将order阶页链接到列表尾 if (page_is_buddy(higher_page, higher_buddy, order + 1)) { list_add_tail(&page->lru, &zone->free_area[order].free_list[migratetype]); goto out; } } 如果高一阶页不可合并就将页块链接到列表首 list_add(&page->lru, &zone->free_area[order].free_list[migratetype]); out: 增加对应阶的空闲页计数 zone->free_area[order].nr_free++; |
|