除了slab分配器,系统中所有正在使用的页面都存放在页面高速缓存中,并且通过page结构体的lru链表在一起,
所以很容易去扫描并替换他们。slab页面没有放在page cache中。
对于每一个zone:都有下面几个参数
struct list_head active_list;
struct list_head inactive_list;
unsigned long nr_scan_active;
unsigned long nr_scan_inactive;
static inline void
add_page_to_active_list(struct zone *zone, struct page *page)
{
list_add(&page->lru, &zone->active_list);
__inc_zone_state(zone, NR_ACTIVE);
}
static inline void
add_page_to_inactive_list(struct zone *zone, struct page *page)
{
list_add(&page->lru, &zone->inactive_list);
__inc_zone_state(zone, NR_INACTIVE);
}
这两个函数非常关键,先列在上面。
内存分配有下面的三种方式:
1:kmalloc/kfree
会从cache_sizes这个通用的cache上申请具体的slab对象或者
会调用__kmem_cache_free( ) , 将当前内存归还到缓存cache_sizes相应slab上
2: alloc_page( ) 和alloc_pages( ) 最终调用__alloc_pages( ) , 使用Buddy伙伴算法, 从
zone- > free_area[ order] 或者拆分order+ 1之上的连续内存, 申请若干页连续的物理内存。
free_pages( ) - > __free_pages_ok( ) 将内存归还到Buddy伙伴算法管理的
zone- > free_area[ order] 上
上面的函数,都应该是真正的分配了物理内存。
3:vmalloc和vfree分配的进程空间连续的虚拟空间,但是“他们对应的物理内存还是要经过缺页中断来分配”,我认为这句话是有问题的,在代码中已经通过alloc-page来分配了物理内存,建立了页表。分配的物理地址的页争也是不连续的。
也就是说 确保页在虚拟地址空间内是连续的。它通过分配非连续的物理内存块,再“修正”页表。但是为什么这里还说需要缺页中断呢?因为一个进程启动的时候,他把父进程的用户空间的pgd拷贝了,但是在执行的时候,访问内核数据或者代码,会发生vmalloc地址错误,然后把init进程的pgd拷贝到该进程,这也保证了共响内核的pgd,然后你这个时候这个地址才可以访问,但是在vmalloc的时候,物理内存已经有了。
代码如下:
static inline int alloc_area_pte (pte_t * pte, unsigned long address,
unsigned long size, int gfp_mask, pgprot_t prot)
{
unsigned long end;
address &= ~PMD_MASK;
end = address + size;
if (end > PMD_SIZE)
end = PMD_SIZE;
do {
struct page * page;
spin_unlock(&init_mm.page_table_lock);
page = alloc_page(gfp_mask);
spin_lock(&init_mm.page_table_lock);
if (!pte_none(*pte))
printk(KERN_ERR "alloc_area_pte: page already exists/n");
if (!page)
return -ENOMEM;
set_pte(pte, mk_pte(page, prot));
address += PAGE_SIZE;
pte++;
} while (address < end);
return 0;
}
从上面很清晰的看到,会call alloc_page这个分配函数,建立页表给init的pgd