一. Android下用户态swap的意义
二. Android下用户态swap的实现方法
1. 首先实现系统调用swap_process
(在Andriod的内核层实现系统调用,可以参考我前面的文章。)
2. $kernel/mm/swap_state.c
定义系统调用函数swap_process,参数为pid(用户层传过来的进程id)
/**
* swap inactive pages of process with pid
*/
SYSCALL_DEFINE1(swap_process, pid_t, pid)
{
struct task_struct* p = NULL;
LIST_HEAD(pagelist);
unsigned long nr_reclaimed_pages = 0;
//1. find the process ds() through pid
printk(KERN_INFO "user pid: %d\n", pid);
p = find_task_by_pid_ns(pid, current->nsproxy->pid_ns);
if(NULL == p)
{
printk(KERN_ERR "+++could not find task_struct with the pid+++\n");
return 0;
}
//2. filter the inactive pages and add them to swap cache
traverse_pages(p, &pagelist);
//3. swap out pages
if(!list_empty(&pagelist)){
//path: mm/vmscan.c
nr_reclaimed_pages = shrink_process_list(&pagelist);
printk(KERN_INFO "reclaimed pages: %lu\n", nr_reclaimed_pages);
}
return (int)nr_reclaimed_pages;
}
遍历进程的地址空间(各个线性区)
/**
* traverse the page frames of the given process
* parameter: process des,
*/
int traverse_pages(struct task_struct* p, struct list_head *pagelist)
{
struct mm_struct* mm = NULL;
struct vm_area_struct* temp_vm = NULL;
struct vm_area_struct* init_vm = NULL;
struct page * reclaim_page = NULL;
unsigned long PAGE_INNER = (1 << PAGE_SHIFT) -1, temp_addr = 0;
int inactive_page_count = 0;
int err = 0;
LIST_HEAD(page_list);
if(!p)
goto out;
mm = p->mm;
if(!mm)
goto out;
down_read(&mm->mmap_sem);
init_vm = mm->mmap;
temp_vm = init_vm;
do{
if(temp_vm->vm_flags)
for(temp_addr=temp_vm->vm_start; temp_addr < temp_vm->vm_end; temp_addr+=PAGE_SIZE)
{
if(!(temp_addr & PAGE_INNER))
{
//reclaim_page = get_inactive_page(mm, temp_addr);
reclaim_page = follow_page(temp_vm, temp_addr, FOLL_GET|FOLL_SPLIT);
if(IS_ERR(reclaim_page) || !reclaim_page)
continue;
err = isolate_lru_page(reclaim_page);
if(!err){
//if(page_lru(reclaim_page) == LRU_INACTIVE_ANON){
list_add_tail(&reclaim_page->lru, pagelist);
inc_zone_page_state(reclaim_page, NR_ISOLATED_ANON + page_is_file_cache(reclaim_page));
inactive_page_count++;
//}
}
}
}
if(temp_vm->vm_next == NULL)
break;
temp_vm = temp_vm->vm_next;
}while(true);
printk(KERN_INFO "swap process success: inactive pages = %d map_count:%d\n", inactive_page_count, mm->map_count);
up_read(&mm->mmap_sem);
return 1;
out:
up_read(&mm->mmap_sem);
printk(KERN_ERR "traverse page error!\n");
return 0;
}
/**
* get inactive pages of the given linear address
* parameter: pgd, linear addr
*/
struct page* get_inactive_page(struct mm_struct* mm, unsigned long addr)
{
struct page* inactive_page = NULL;
pgd_t* pgd;
pmd_t* pmd;
pte_t* pte;
pgd = pgd_offset(mm, addr);
if(pgd_none(*pgd) || pgd_bad(*pgd))
goto out;
//pmd = pmd_offset(pgd, addr);
pmd = (pmd_t*) pgd;
if(pmd_none(*pmd) || pmd_bad(*pmd))
goto out;
pte = pte_offset_map(pmd, addr);
if(!pte)
goto out;
inactive_page = pte_page(*pte);
if(page_lru(inactive_page) == LRU_INACTIVE_ANON)
return inactive_page;
out:
printk(KERN_ERR "+++walk through invalid page table+++\n");
return NULL;
}
将指定的页面链表换出到flash swap区域
unsigned long shrink_process_list(struct list_head *pagelist)
{
int priority = 0;
unsigned long ret_nr_dirty = 0, ret_nr_writeback = 0, nr_reclaimed_pages = 1;
struct zone *zone;
struct scan_control sc = {
.gfp_mask = GFP_USER,
.may_writepage = !laptop_mode,
.nr_to_reclaim = SWAP_CLUSTER_MAX,
.may_unmap = 1,
.may_swap = 1,
.order = 0,
.target_mem_cgroup = NULL,
};
struct mem_cgroup *root = sc.target_mem_cgroup;
struct mem_cgroup_reclaim_cookie reclaim = {
.priority = priority,
};
struct mem_cgroup *memcg;
struct mem_cgroup_zone mz;
struct page *page = lru_to_page(pagelist);
zone = page_zone(page);
reclaim.zone = zone;
memcg = mem_cgroup_iter(root, NULL, &reclaim);
mz.mem_cgroup = memcg;
mz.zone = zone;
nr_reclaimed_pages = shrink_page_list(pagelist, &mz, &sc, priority, &ret_nr_dirty, &ret_nr_writeback);
return nr_reclaimed_pages;
}