ksm的全称是kernel samepage mergeing,主要用于合并内容相同的页面,只要用于虚拟机中 存在的大量的冗余页面。 在kernel中可以通过PageKsm 来判断一个页面是否是ksm页面 static __always_inline int PageKsm(struct page *page) { page = compound_head(page); return ((unsigned long)page->mapping & PAGE_MAPPING_FLAGS) == PAGE_MAPPING_KSM; } 从makefile中可以知道,kernel要使能这个特定的话,必须打开CONFIG_KSM obj-$(CONFIG_KSM) += ksm.o kernel中会创建一个thread来专门做这件事 static int __init ksm_init(void) { struct task_struct *ksm_thread; int err; /* The correct value depends on page size and endianness */ zero_checksum = calc_checksum(ZERO_PAGE(0)); /* Default to false for backwards compatibility */ ksm_use_zero_pages = false; err = ksm_slab_init(); if (err) goto out; #这里有新建一个thread 来合并具有相同页面的page ksm_thread = kthread_run(ksm_scan_thread, NULL, "ksmd"); if (IS_ERR(ksm_thread)) { pr_err("ksm: creating kthread failed\n"); err = PTR_ERR(ksm_thread); goto out_free; } return 0; } 我们看看其线程的回调函数ksm_scan_thread static int ksm_scan_thread(void *nothing) { set_freezable(); set_user_nice(current, 5); while (!kthread_should_stop()) { mutex_lock(&ksm_thread_mutex); wait_while_offlining(); if (ksmd_should_run()) #如果运行thread运行的话,这里调用ksm_do_scan 来找到相同的页面并合并,这里的形参ksm_thread_pages_to_scan #表示每次合并的页数 ksm_do_scan(ksm_thread_pages_to_scan); mutex_unlock(&ksm_thread_mutex); try_to_freeze(); if (ksmd_should_run()) { #目前ksm_thread_sleep_millisecs 等于20ms。可以知道这个线程每20ms运行一次 schedule_timeout_interruptible( msecs_to_jiffies(ksm_thread_sleep_millisecs)); } else { wait_event_freezable(ksm_thread_wait, ksmd_should_run() || kthread_should_stop()); } } return 0; } static void ksm_do_scan(unsigned int scan_npages) { struct rmap_item *rmap_item; struct page *uninitialized_var(page); while (scan_npages-- && likely(!freezing(current))) { cond_resched(); #rmap 表示通过反向映射的页面,这里找到一个需要合并的页面 rmap_item = scan_get_next_rmap_item(&page); if (!rmap_item) return; #开始合并页面 cmp_and_merge_page(page, rmap_item); put_page(page); } } 这里需要支持 用户申请的页面只有再通过系统调用madvise显示的加入到ksm中,才会被线程ksm_scan_thread 扫描并合并 madvise_behavior->ksm_madvise->__ksm_enter int __ksm_enter(struct mm_struct *mm) { struct mm_slot *mm_slot; int needs_wakeup; #分配一个struct mm_slot数据结构 mm_slot = alloc_mm_slot(); if (!mm_slot) return -ENOMEM; /* Check ksm_run too? Would need tighter locking */ needs_wakeup = list_empty(&ksm_mm_head.mm_list); spin_lock(&ksm_mmlist_lock); insert_to_mm_slots_hash(mm, mm_slot); #将mm_slot 添加到ksm_scan.mm_slot->mm_list 中 if (ksm_run & KSM_RUN_UNMERGE) list_add_tail(&mm_slot->mm_list, &ksm_mm_head.mm_list); else list_add_tail(&mm_slot->mm_list, &ksm_scan.mm_slot->mm_list); spin_unlock(&ksm_mmlist_lock); #设置标志位,表示这个进程已经被添加到ksm系统中 set_bit(MMF_VM_MERGEABLE, &mm->flags); mmgrab(mm); #唤醒前面的ksm_scan_thread 线程来扫描相同页面并merge if (needs_wakeup) wake_up_interruptible(&ksm_thread_wait); return 0; }
kernel中ksm特性
猜你喜欢
转载自blog.csdn.net/tiantao2012/article/details/80484209
今日推荐
周排行