前面已经讲过,当pte所对应的page不在内存中,且pte对应的内容不为0时,表示此时pte的内容所对应的页面在swap空间中,缺页异常时会通过do_swap_page()函数来分配页面:
static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long address, pte_t *page_table, pmd_t *pmd,
unsigned int flags, pte_t orig_pte)
{
…………
entry = pte_to_swp_entry(orig_pte);----------(1)
page = lookup_swap_cache(entry);----------(2)
if (!page) {
page = swapin_readahead(entry,
GFP_HIGHUSER_MOVABLE, vma, address);-------(3)
…………
ret = VM_FAULT_MAJOR;
count_vm_event(PGMAJFAULT);-------------(4)
…………
}
locked = lock_page_or_retry(page, mm, flags);--------------(5)
…………
page_table = pte_offset_map_lock(mm, pmd, address, &ptl);--------------(6)
…………
inc_mm_counter_fast(mm, MM_ANONPAGES);--------------(7)
dec_mm_counter_fast(mm, MM_SWAPENTS);---------------(8)
pte = mk_pte(page, vma->vm_page_prot);
set_pte_at(mm, address, page_table, pte);---------------(9)
if (page == swapcache) {---------(10)
do_page_add_anon_rmap(page, vma, address, exclusive);
mem_cgroup_commit_charge(page, memcg, true);
} else { /* ksm created a completely new copy */
page_add_new_anon_rmap(page, vma, address);-------------------(11)
mem_cgroup_commit_charge(page, memcg, false);
lru_cache_add_active_or_unevictable(page, vma);------------------(12)
}
…………
}
(1)根据pte来获取swap的entry,swap entry和pte有一个对应关系,和__SWP_TYPE_SHIFT __SWP_TYPE_BITS两个宏相关,具体对应关系,这里不做深究
(2)在swapcache里面寻找entry对应的page
(3)如果swapcache里面找不到就在swap area里面找
(4)PGMAJFAULT计数加1
(5)尝试给page上锁
(6)获取一个pte entry
(7)anonpage数加1,匿名页从swap空间交换出来,所以加1
(8)swap page个数减1,由page和VMA属性创建一个新的pte。
(9)将新生成的PTE entry添加到硬件页表
(10)根据page是否为swapcache,如果是,则只为page创建rmap,如果不是则创建rmap还要将page添加到LRU链表。
总结一下:整个过程就是由pte得到swap entry,再由swap entry得到page,再由pte以及pte entry添加到硬件pte页表。
另外这里由关于swapcache的概念,在网上找的,讲得比较好:
这个swap cache的作用不是说要加快磁盘的I/O效率,主要是为了防止页面在swap in和swap out时,进程的同步问题,也就是在进行swap out操作时(将页面内容写入磁盘分区时)进程如果发起了对换出页面的访问,系统对其的处理。有了swap cache的存在,如果页面的数据还没有完全写入磁盘时,这个page frame是在swap cache(swap cache有个引用指向页面),等数据完全写入磁盘后,而且没有进程对page frame进行访问,那么swap cache才会释放page frame,将其交给buddy system.
并不是每一个匿名页都在swap cache中,只有以下情形之一的匿名页才在:
- 匿名页即将被swap-out时会先被放进swap cache,但通常只存在很短暂的时间,因为紧接着在pageout完成之后它就会从swap cache中删除,毕竟swap-out的目的就是为了腾出空闲内存;
【注:参见mm/vmscan.c: shrink_page_list(),它调用的add_to_swap()会把swap cache页面标记成dirty,然后它调用try_to_unmap()将页面对应的page table mapping都删除,再调用pageout()回写dirty page,最后try_to_free_swap()会把该页从swap cache中删除。】 - 曾经被swap-out现在又被swap-in的匿名页会在swap cache中,直到页面中的内容发生变化、或者原来用过的交换区空间被回收为止。
【注:当匿名页的内容发生变化时会删除对应的swap cache,代码参见mm/swapfile.c: reuse_swap_page()。】
SwapCached背后的含义是:系统中有多少匿名页曾经被swap-out、现在又被swap-in并且swap-in之后页面中的内容一直没发生变化。也就是说,如果这些匿名页需要被swap-out的话,是无需进行I/O write操作的。