线性地址-->物理地址
若映射已经建立,但相应页表|页目录的P(present)位为0,表示相应的物理页面不再内存,从而无法访问内存。
对于这种情况和未建立映射的情况,CPU的MMU硬件不对其进行区分,都进行“页面异常”处理
在handle_page_fault函数中:
说明:
首先区分的是pte_present,也就是检查表项的P标志位,查看物理页面是否在内存中。
若不在内存中,则通过pte_none检测表项是否为空(all 0),
若为空,则说明页面的映射尚未建立,调用do_no_page
如果非空,则说明映射已经建立,只是物理页面不再内存中,调用do_swap_page,将该页面换入内存
代码:
static inline int handle_pte_fault(struct mm_struct *mm,
struct vm_area_struct * vma, unsigned long address,
int write_access, pte_t * pte)
{
pte_t entry;
/*
* We need the page table lock to synchronize with kswapd
* and the SMP-safe atomic PTE updates.
*/
spin_lock(&mm->page_table_lock);
entry = *pte;
if (!pte_present(entry)) {
/*
* If it truly wasn't present, we know that kswapd
* and the PTE updates will not touch it later. So
* drop the lock.
*/
spin_unlock(&mm->page_table_lock);
if (pte_none(entry))
return do_no_page(mm, vma, address, write_access, pte);
return do_swap_page(mm, vma, address, pte, pte_to_swp_entry(entry), write_access);
}
do_swap_page函数代码:
说明:
address为映射失败的线性地址
page_table为映射失败的页面表项,entry为表项内容
(
当页面在物理内存中时,页面表项为pte_t结构
当page不再物理内存时,页面表项为swap_entry_t结构)
write_access表示映射失败时所进行的访问种类(read/write)
lookup_swap_cache:先检查页面是否在换进/换出队列中(尚未换出到磁盘)
swapin_readahead:从磁盘上预读多个页面到 换进/换出队列 和 活跃页面队列(效率问题)
预读可能由于无法分配到足够页面而失败(重读,并只读entry页面)
read_swap_cache:从活跃页面队列读取entry
代码:
static int do_swap_page(struct mm_struct * mm,
struct vm_area_struct * vma, unsigned long address,
pte_t * page_table, swp_entry_t entry, int write_access)
{
struct page *page = lookup_swap_cache(entry);
pte_t pte;
if (!page) {
lock_kernel();
swapin_readahead(entry);
page = read_swap_cache(entry);
unlock_kernel();
if (!page)
return -1;
flush_page_to_ram(page);
flush_icache_page(vma, page);
}
mm->rss++;
pte = mk_pte(page, vma->vm_page_prot);
/*
* Freeze the "shared"ness of the page, ie page_count + swap_count.
* Must lock page before transferring our swap count to already
* obtained page count.
*/
lock_page(page);
swap_free(entry);
if (write_access && !is_page_shared(page))
pte = pte_mkwrite(pte_mkdirty(pte));
UnlockPage(page);
set_pte(page_table, pte);
/* No need to invalidate - it was non-present before */
update_mmu_cache(vma, address, pte);
return 1; /* Minor fault */
- }
read_swap_cache函数:
说明:
read_swap_cache函数只是调用了read_swap_cache_async(),并把参数wait设置为1
lookup_swap_cache执行了两次
第一次:因为swapin_readahead已经把目标读取进来,需要在swaper_space队列中寻找一次
第二次:lookup_swap_cache并未找到页面,使用__get_free_page申请页面,
但是申请过程中可能会被阻塞(可能有其他进程将entry换入内存),需要再次
在swaper_space和avtive_list中查找一次,若找到说明被其他进程换入,
跳转到out_free_page,释放__get_free_page申请的页面,
并使用swap_free减少entry的共享计数(swap_duplicate(entry)增加了共享计数)
代码:
struct page * read_swap_cache_async(swp_entry_t entry, int wait)
{
struct page *found_page = 0, *new_page;
unsigned long new_page_addr;
/*
*Make sure the swap entry is still in use.
*/
if(!swap_duplicate(entry)) /* Account for the swap cache */
goto out;
/*
*Look for the page in the swap cache.
*/
found_page = lookup_swap_cache(entry);
if (found_page)
goto out_free_swap;
new_page_addr = __get_free_page(GFP_USER);
if (!new_page_addr)
goto out_free_swap;
/*Out of memory */
new_page= virt_to_page(new_page_addr);
/*
*Check the swap cache again, in case we stalled above.
*/
found_page = lookup_swap_cache(entry);
if(found_page)
goto out_free_page;
/*
*Add it to the swap cache and read its contents.
*/
lock_page(new_page);
add_to_swap_cache(new_page, entry);
rw_swap_page(READ, new_page, wait);
return new_page;
- out_free_page:
page_cache_release(new_page);
out_free_swap:
swap_free(entry);
out:
return found_page;
}