再读内核存储管理(6):高速缓存的应用

来源:互联网 发布:南京逍遥游网络怎么样 编辑:程序博客网 时间:2024/05/22 06:17
 
 
快乐虾
http://blog.csdn.net/lights_joy/
lights@hb165.com
  
 
本文适用于
ADI bf561 DSP
uclinux-2008r1-rc8 (移植到vdsp5)
Visual DSP++ 5.0
 
  
欢迎转载,但请保留作者信息
 
1.1    高速缓存的应用
内核经常需要请求和释放单个页面。为了提升系统性能,每个内存管理区zone定义了一个“每CPU”页面高速缓存。所有高速缓存包含一些预先分配的页,它们被用于满足本地CPU发出的单一内存请求。
实际上,这里为每个内存管理区和每个CPU提供了两个高速缓存:一个热高速缓存,它存放的页框中所包含的内容很可能就在CPU硬件高速缓存中;还有一个冷高速缓存。
内核使用两个位标来监视热高速缓存和冷高速缓存的大小:如果页个数低于下界low,内核通过buddy系统分配batch个单一页面来补充对应的高速缓存;否则,如果页框个数高过上界high,内核从高速缓存中释放batch个页框到buddy系统中。
1.1.1   页面回收
当buddy算法回收一个页时,它会首先试图将其放在热高速缓存中,其实现如下:
fastcall void __free_pages(struct page *page, unsigned int order)
{
     if (put_page_testzero(page)) {
         if (order == 0)
              free_hot_page(page);
         else
              __free_pages_ok(page, order);
     }
}
void fastcall free_hot_page(struct page *page)
{
     free_hot_cold_page(page, 0);
}
跟踪free_hot_cold_page函数:
/*
 * Free a 0-order page
 */
static void fastcall free_hot_cold_page(struct page *page, int cold)
{
     struct zone *zone = page_zone(page); // 返回ZONE_DMA这个区域
     struct per_cpu_pages *pcp;
     unsigned long flags;
 
     if (PageAnon(page))
         page->mapping = NULL;
     if (free_pages_check(page))
         return;
 
     if (!PageHighMem(page)) // 总为FALSE
         debug_check_no_locks_freed(page_address(page), PAGE_SIZE);
     arch_free_page(page, 0);    // 空语句
     kernel_map_pages(page, 1, 0);    // 空语句
 
     pcp = &zone_pcp(zone, get_cpu())->pcp[cold];
     local_irq_save(flags);
     __count_vm_event(PGFREE); // 空语句
     list_add(&page->lru, &pcp->list);
     pcp->count++;
     if (pcp->count >= pcp->high) {
         free_pages_bulk(zone, pcp->batch, &pcp->list, 0);
         pcp->count -= pcp->batch;
     }
     local_irq_restore(flags);
     put_cpu();
}
从这个函数可以看出,如果pcp中的页数较少的时候,它将直接把回收的页面放在高速缓存中(热高速缓存或者冷高速缓存),即上述函数中的list_add调用。在这里pcp实际指向DMA_ZONE中的pcp。当高速缓存的页面较多时,它将切换出部分最后进入缓存的页,将这些页链接到可用的页面链表中(使用BUDDY算法)。
1.1.2   换页策略
内核中当高速缓存的页面较多时,会将部分页面切换到可用内存的链表中,这个操作由free_pages_bulk函数完成:
/*
 * Frees a list of pages.
 * Assumes all pages on list are in same zone, and of same order.
 * count is the number of pages to free.
 *
 * If the zone was previously in an "all pages pinned" state then look to
 * see if this freeing clears that state.
 *
 * And clear the zone's pages_scanned counter, to hold off the "all pages are
 * pinned" detection logic.
 */
static void free_pages_bulk(struct zone *zone, int count,
                       struct list_head *list, int order)
{
     spin_lock(&zone->lock);
     zone->all_unreclaimable = 0;
     zone->pages_scanned = 0;
     while (count--) {
         struct page *page;
 
         VM_BUG_ON(list_empty(list));
         page = list_entry(list->prev, struct page, lru);
         /* have to delete it as __free_one_page list manipulates */
         list_del(&page->lru);
         __free_one_page(page, zone, order);
     }
     spin_unlock(&zone->lock);
}
因为在页面回收时是将要回收的页面插入到双链表的表头,而从上述函数中可以看出,在将页面切换出高速缓存的时候,也是从链表头按顺序进行的,因此整个切换策略就是后进先出,即最后进入高速缓存的先切换出去。
 
原创粉丝点击