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特性
最新推荐文章于 2023-12-12 11:48:14 发布