kernel中ksm特性

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;
}

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页