轉載自:http://blog.csdn.net/haitaoliang/article/details/25003395
Ksm介紹
2.6.32引入了KSM(KernelSamepage Merging)允許這個系統管理程序通過合併內存頁面來增加併發虛擬機的數量。VMware的ESX服務器系統管理程序將這個特性命名爲TransparentPage Sharing (TPS),而XEN將其稱爲MemoryCoW。不管採用哪種名稱和實現,這個特性都提供了更好的內存利用率,從而允許操作系統(KVM的系統管理程序)過量使用內存,支持更多的應用程序或VM。
假如操作系統和應用程序代碼以及常量數據在VMs之間相同,那麼這個特點就很有用。當頁面惟一時,它們可以被合併,從而釋放內存,供其他應用程序使用。
一臺主機(Host)同時運行好幾個相同類型的實例(guests),通過這種技術共享相同代碼,比如每個guest的核心代碼,那麼隨着guest實例增加,Host內存不會急劇的下降,有效的增加Host的provisioning能力。同時釋放出內存,供其他系統或程序使用。這也是我們如何能夠讓一臺16G內存的Server跑起52臺1G內存XP系統的方法。VMware的ESX/ESXi將這個特性定義爲TransparentPage Sharing (TPS),而XEN則將其稱爲MemoryCoW(Copy-on-Write)。存儲技術中的這種技術我們稱爲去耦合(de-duplication)。去耦合這種技術通過刪除冗餘數據(基於數據塊,或者基於更大的數據片段,比如文件)來減少已存儲的數據。公共數據片段被合併(以一種copy-on-write[CoW] 方式),釋放空間供其他用途。使用這種方法,存儲成本更低,最終需要的存儲器也更少。鑑於當前的數據增長速度,這個功能顯得非常重要。
儘管Linux中的內存共享在虛擬環境中有優勢(KSM最初設計用於基於內核的虛擬機),但它在非虛擬環境中仍然有用。事實上,KSM甚至在嵌入式Linux系統中也有用處,表明了這種方法的靈活性。
KSM作爲內核中的守護進程(稱爲ksmd)存在。它定期執行頁面掃描,識別副本頁面併合並副本,釋放這些頁面以供它用。
KSM執行上述操作的過程對用戶透明。例如,副本頁面被合併,然後被標記爲只讀,但是,如果這個頁面的其中一個用戶由於某種原因更改該頁面,該用戶將以CoW方式收到自己的副本。可以在內核源代碼./mm/ksm.c 中找到KSM內核模塊的完整實現。
KSM應用程序編程接口(API)通過madvise系統調用和一個新的推薦參數MADV_MERGEABLE(表明已定義的區域可以合併)來實現。可以通過MADV_UNMERGEABLE參數(立即從一個區域取消合併任何已合併頁面)從可合併狀態刪除一個區域。注意,通過madvise來刪除一個頁面區域可能會導致一個EAGAIN錯誤,因爲該操作可能會在取消合併過程中耗盡內存,從而可能會導致更大的麻煩(內存不足情況)。
一旦某個區域被定義爲“可合併”,KSM將把該區域添加到它的工作內存列表。啓用KSM時,它將搜索相同的頁面,以寫保護的CoW方式保留一個頁面,釋放另一個頁面以供它用。
KSM使用的方法與內存去耦合中使用的方法不同。在傳統的去耦合中,對象被散列化,然後使用散列值進行初始相似性檢查。當散列值一致時,下一步是進行一個實際對象比較(本例中是一個內存比較),以便正式確定這些對象是否一致。KSM在它的第一個實現中採用這種方法,但後來開發了一種更直觀的方法來簡化它。
在當前的KSM中,頁面通過兩個“紅-黑”樹管理,其中一個 “紅-黑”樹是臨時的。第一個樹稱爲不穩定樹,用於存儲還不能理解爲穩定的新頁面。換句話說,作爲合併候選對象的頁面(在一段時間內沒有變化)存儲在這個不穩定樹中。不穩定樹中的頁面不是寫保護的。第二個樹稱爲穩定樹,存儲那些已經發現是穩定的且通過KSM合併的頁面。爲確定一個頁面是否是穩定頁面,KSM使用了一個簡單的32位校驗和(checksum)。當一個頁面被掃描時,它的校驗和被計算且與該頁面存儲在一起。在一次後續掃描中,如果新計算的校驗和不等於此前計算的校驗和,則該頁面正在更改,因此不是一個合格的合併候選對象。
使用KSM進程處理一個單一的頁面時,第一步是檢查是否能夠在穩定樹中發現該頁面。搜索穩定樹的過程很有趣,因爲每個頁面都被視爲一個非常大的數字(頁面的內容)。一個memcmp(內存比較)操作將在該頁面和相關節點的頁面上執行。如果memcmp返回0,則頁面相同,發現一個匹配值。反之,如果memcmp返回-1,則表示候選頁面小於當前節點的頁面;如果返回1,則表示候選頁面大於當前節點的頁面。儘管比較4KB的頁面似乎是相當重量級的比較,但是在多數情況下,一旦發現一個差異,memcmp將提前結束。
如果候選頁面位於穩定樹中,則該頁面被合併,候選頁面被釋放。反之,如果沒有發現候選頁面,則應轉到不穩定樹。
在不穩定樹中搜索時,第一步是重新計算頁面上的校驗和。如果該值與原始校驗和不同,則本次掃描的後續搜索將拋棄這個頁面(因爲它更改了,不值得跟蹤)。如果校驗和沒有更改,則會搜索不穩定樹以尋找候選頁面。不穩定樹的處理與穩定樹的處理有一些不同。第一,如果搜索代碼沒有在不穩定樹中發現頁面,則在不穩定樹中爲該頁面添加一個新節點。但是如果在不穩定樹中發現了頁面,則合併該頁面,然後將該節點遷移到穩定樹中。
當掃描完成時,穩定樹被保存下來,但不穩定樹則被刪除並在下一次掃描時重新構建。這個過程大大簡化了工作,因爲不穩定樹的組織方式可以根據頁面的變化而變化。由於穩定樹中的所有頁面都是寫保護的,因此當一個頁面試圖被寫入時將生成一個頁面故障,從而允許CoW進程爲寫入程序取消頁面合併(break_cow())。穩定樹中的孤立頁面將在稍後被刪除。
Ksm實現
staticint __init ksm_init(void) 用於ksm初始化:
1、創建ksmd線程,該線程用於物理頁面掃描合併:
ksm_thread=kthread_run(ksm_scan_thread, NULL, "ksmd");
2、創建sysfs的配置和監控,下章節介紹:
err= sysfs_create_group(mm_kobj,&ksm_attr_group);
if(err) {
printk(KERN_ERR"ksm:register sysfs failed\n");
kthread_stop(ksm_thread);
gotoout_free2;
}
主函數ksm_scan_thread爲一個非實時任務,循環進行頁合併操作:
staticintksm_scan_thread(void *nothing)
{
set_user_nice(current,5); 設置成nice值爲5非實時任務
while(!kthread_should_stop()) {
mutex_lock(&ksm_thread_mutex);
if(ksmd_should_run())
ksm_do_scan(ksm_thread_pages_to_scan);一次掃描成功ksm_thread_pages_to_scan頁返回
mutex_unlock(&ksm_thread_mutex);
if(ksmd_should_run()) {
schedule_timeout_interruptible(
msecs_to_jiffies(ksm_thread_sleep_millisecs));休息ksm_thread_sleep_millisecs時間後進行下一次掃描
}else {
wait_event_interruptible(ksm_thread_wait,
ksmd_should_run()||kthread_should_stop());
}
}
return0;
}
首先介紹一個數據結構rmap_item反向映射條目,該條目保存有物理地址到虛擬地址的反向映射,同時用來組織穩定樹的紅黑樹結構,對於非穩定樹保存有校驗和。
structrmap_item{
structlist_head link;
structmm_struct *mm;
unsignedlong address; /* + low bits used for flags below*/
union{
unsignedint oldchecksum; /* when unstable */
structrmap_item *next; /* when stable */
};
union{
structrb_node node; /* when tree node */
structrmap_item *prev; /* in stable list */
};
};
頁合併主函數ksm_do_scan首先根據掃描區間獲得一個page,併爲其建立反向映射條目。接着對該頁在穩定樹和非穩定樹中尋找相同的頁進行比較與合併。
staticvoidksm_do_scan(unsigned int scan_npages)
{
structrmap_item *rmap_item;
structpage *page;
while(scan_npages--) {
cond_resched();
rmap_item=scan_get_next_rmap_item(&page);//對掃描區間的每一個頁進行掃描
if(!rmap_item)
return;
if(!PageKsm(page)||!in_stable_tree(rmap_item))//已經在穩定樹中不必考慮,PageKsm是ksm機制引入的,replace_page中對添加的對穩定樹頁的特殊標誌
cmp_and_merge_page(page,rmap_item);//對該頁在穩定樹和非穩定樹中尋找相同的頁進行比較與合併
elseif (page_mapcount(page)== 1) {//當此時頁的引用計數爲1時,將頁從穩定樹上摘除,成爲普通頁
/*
*Replace now-unshared ksm page by ordinarypage.
*/
break_cow(rmap_item->mm,rmap_item->address);
remove_rmap_item_from_tree(rmap_item);
rmap_item->oldchecksum=calc_checksum(page);//計算校驗和
}
put_page(page);
}
}
瞭解scan_get_next_rmap_item前,首先介紹一個數據結構:
structksm_scan{
structmm_slot *mm_slot; //當前正掃描mm空間
unsignedlong address;//當前正掃描地址
structrmap_item *rmap_item;//爲當前頁面建立的反向映射條目(用於操作樹)
};
scan_get_next_rmap_item根據掃描區間獲得一個page,併爲其建立反向映射條目。
staticstructrmap_item *scan_get_next_rmap_item(struct page **page)
{
structmm_struct *mm;
structmm_slot *slot;
structvm_area_struct *vma;
structrmap_item *rmap_item;
if(list_empty(&ksm_mm_head.mm_list))//掃描區間不爲0
returnNULL;
slot=ksm_scan.mm_slot;獲取當前掃描空間
if(slot ==&ksm_mm_head) {//每輪詢一個循環建立一個root_unstable_tree表,重新初始化
root_unstable_tree=RB_ROOT;
… …
next_mm:
ksm_scan.address= 0;
ksm_scan.rmap_item=list_entry(&slot->rmap_list,
structrmap_item,link);
}
mm= slot->mm;
down_read(&mm->mmap_sem);
if(ksm_test_exit(mm))
vma= NULL;
else
vma= find_vma(mm,ksm_scan.address);
for(; vma; vma = vma->vm_next) {
if(!(vma->vm_flags& VM_MERGEABLE))//沒有VM_MERGEABLE標誌,不能合併
continue;
if(ksm_scan.address < vma->vm_start)//從vm_start開始掃描
ksm_scan.address=vma->vm_start;
if(!vma->anon_vma)//只掃描匿名頁
ksm_scan.address=vma->vm_end;
while(ksm_scan.address <vma->vm_end) {將vma區全部掃描
if(ksm_test_exit(mm))
break;
*page= follow_page(vma, ksm_scan.address,FOLL_GET);//一次快速的get_page
if(*page &&PageAnon(*page)) {
flush_anon_page(vma,*page,ksm_scan.address);
flush_dcache_page(*page);
rmap_item=get_next_rmap_item(slot,//get_next_rmap_item調用alloc_rmap_item建立一個rmap_item條目並插入到鏈表末尾
ksm_scan.rmap_item->link.next,
ksm_scan.address);
if(rmap_item){
ksm_scan.rmap_item=rmap_item;
ksm_scan.address+=PAGE_SIZE;
}else
put_page(*page);
up_read(&mm->mmap_sem);
returnrmap_item;
}
if(*page)
put_page(*page);
ksm_scan.address+=PAGE_SIZE;
cond_resched();
}
}
if(ksm_test_exit(mm)) {
ksm_scan.address= 0;
ksm_scan.rmap_item=list_entry(&slot->rmap_list,
structrmap_item,link);
}
/*
*Nuke all the rmap_items that are above thiscurrent rmap:
*because there were no VM_MERGEABLE vmas withsuch addresses.
*/
remove_trailing_rmap_items(slot,ksm_scan.rmap_item->link.next);
spin_lock(&ksm_mmlist_lock);
ksm_scan.mm_slot=list_entry(slot->mm_list.next,//根據鏈表得到下一個掃描空間
structmm_slot,mm_list);
……
/*Repeat until we've completedscanning the whole list */
slot=ksm_scan.mm_slot;
if(slot !=&ksm_mm_head)//如果掃描沒有到頭,繼續掃描
gotonext_mm;
ksm_scan.seqnr++;
returnNULL;
}
cmp_and_merge_page經過scan_get_next_rmap_item掃描到的item尋找相同的頁進行比較與合併。
首先嚐試是否與穩定樹的頁一致,如果不行檢查校驗和看頁是否有修改,如果沒有修改合入不穩定樹,如果與不穩定樹的頁一致,則合入穩定樹。
staticvoidcmp_and_merge_page(struct page *page, struct rmap_item *rmap_item)
{
structpage *page2[1];
structrmap_item *tree_rmap_item;
unsignedint checksum;
interr;
……s
tree_rmap_item=stable_tree_search(page, page2, rmap_item);//首先檢查page內容是否與一個穩定樹中的頁相同。
if(tree_rmap_item) {
if(page == page2[0]) //尋找到的是同一個物理頁不必處理
err= 0;
else
err= try_to_merge_with_ksm_page(rmap_item->mm,
//try_to_merge_with_ksm_page用來將oldpage合併至newpage的物理頁
rmap_item->address,
page,page2[0]);
put_page(page2[0]);
if(!err) {
/*
*The page was successfully merged:
*add its rmap_item to the stable tree.
*/
stable_tree_append(rmap_item,tree_rmap_item);//物理頁合併成功後將rmap_item插入穩定樹條目
}
return;
}
/*
*A ksm page might have got here by fork, butits other
*references have already been removed fromthe stable tree.
*Or it might be left over from a break_ksmwhich failed
*when the mem_cgroup had reached its limit:try again now.
*/
if(PageKsm(page))
break_cow(rmap_item->mm,rmap_item->address);
/*
*In case the hash value of the page waschanged from the last time we
*have calculated it, this page to be changedfrequely, therefore we
*don't want to insert it to the unstabletree, and we don't want to
*waste our time to search if there is somethingidentical to it there.
*/
checksum=calc_checksum(page);//計算hash值
if(rmap_item->oldchecksum!= checksum) {//校驗和失敗,頁被修改,屬於經常被修改的頁
rmap_item->oldchecksum=checksum;
return;
}
tree_rmap_item=unstable_tree_search_insert(page, page2, rmap_item);//嘗試比較並插入不穩定樹
if(tree_rmap_item) {
err=try_to_merge_two_pages(rmap_item->mm,
//try_to_merge_two_pages調用try_to_merge_with_ksm_page(同上)將兩個不穩定物理頁合二爲一
rmap_item->address,page,
tree_rmap_item->mm,
tree_rmap_item->address,page2[0]);
/*
*As soon as we merge this page, we want toremove the
*rmap_item of the page we have merged withfrom the unstable
*tree, and insert it instead as new node inthe stable tree.
*/
if(!err) {
rb_erase(&tree_rmap_item->node,&root_unstable_tree);//成功合併之後從不穩定樹刪除該節點
tree_rmap_item->address&=~NODE_FLAG;
ksm_pages_unshared--;
/*
*If we fail to insert the page into thestable tree,
*we will have 2 virtual addresses that arepointing
*to a ksm page left outside the stable tree,
*in which case we need to break_cow on both.
*/
if(stable_tree_insert(page2[0],tree_rmap_item))//條目加入到穩定數
stable_tree_append(rmap_item,tree_rmap_item);
else{
break_cow(tree_rmap_item->mm,//合併至穩定樹失敗則分別寫時複製一分爲二
tree_rmap_item->address);
break_cow(rmap_item->mm,rmap_item->address);
}
}
put_page(page2[0]);
}
}
calc_checksum計算一個頁面的hash值,最終將這個hash值存入rmap_item結構的oldchechsum中,如果再次計算的時候這個值變了,那麼就說明頁面被寫了:
staticinlineu32 calc_checksum(struct page *page)
{
u32checksum;
void*addr= kmap_atomic(page, KM_USER0); //臨時映射到KM_USER0
checksum= jhash(addr, PAGE_SIZE, 17); //計算這個頁面的內容的hash值
kunmap_atomic(addr,KM_USER0);//釋放
returnchecksum;//返回這個hash值
}
stable_tree_search用來檢查page內容是否與一個穩定樹中的頁相同:
staticstructrmap_item *stable_tree_search(struct page *page,
structpage **page2,
structrmap_item *rmap_item)
{
structrb_node*node = root_stable_tree.rb_node;//root_stable_tree是穩定樹表
while(node) {
structrmap_item*tree_rmap_item, *next_rmap_item;
intret;
tree_rmap_item= rb_entry(node, structrmap_item, node);//輪詢穩定樹條目
while(tree_rmap_item) {
BUG_ON(!in_stable_tree(tree_rmap_item));
cond_resched();
page2[0]=get_ksm_page(tree_rmap_item);//根據條目取出一個物理頁面
if(page2[0])
break;
next_rmap_item=tree_rmap_item->next;
remove_rmap_item_from_tree(tree_rmap_item);//取不出說明物理頁已經不存在,ok從穩定樹中刪除反向映射條目
tree_rmap_item=next_rmap_item;
}
if(!tree_rmap_item)
returnNULL;
ret= memcmp_pages(page, page2[0]);//逐bit比較是否同樣的物理頁
if(ret < 0) {
put_page(page2[0]);//不一樣的話則從紅黑樹繼續尋找
node=node->rb_left;
}else if (ret > 0) {
put_page(page2[0]);//不一樣的話則從紅黑樹繼續尋找
node=node->rb_right;
}else {
returntree_rmap_item;//同樣,則返回隨後合併之
}
}
returnNULL;
}
try_to_merge_with_ksm_page用來將oldpage合併至newpage的物理頁:
try_to_merge_with_ksm_page->try_to_merge_one_page->replace_page
staticintreplace_page(struct vm_area_struct *vma, struct page *oldpage,
structpage*newpage, pte_t orig_pte)
{
structmm_struct *mm = vma->vm_mm;
pgd_t*pgd;
pud_t*pud;
pmd_t*pmd;
pte_t*ptep;
spinlock_t*ptl;
unsignedlong addr;
pgprot_tprot;
interr = -EFAULT;
prot=vm_get_page_prot(vma->vm_flags & ~VM_WRITE);
addr= page_address_in_vma(oldpage,vma);
if(addr == -EFAULT)
gotoout;
pgd= pgd_offset(mm, addr);
if(!pgd_present(*pgd))
gotoout;
pud= pud_offset(pgd, addr);
if(!pud_present(*pud))
gotoout;
pmd= pmd_offset(pud, addr);
if(!pmd_present(*pmd))
gotoout;
ptep= pte_offset_map_lock(mm, pmd,addr, &ptl);
if(!pte_same(*ptep, orig_pte)) {//以上驗證oldpage頁表存在,並上鎖
pte_unmap_unlock(ptep,ptl);
gotoout;
}
get_page(newpage);
page_add_ksm_rmap(newpage);//增加newpage匿名頁計數
flush_cache_page(vma,addr,pte_pfn(*ptep));
ptep_clear_flush(vma,addr, ptep);
set_pte_at_notify(mm,addr,ptep, mk_pte(newpage, prot));//修改oldpage的頁表替換成newpage的頁表。
page_remove_rmap(oldpage);//釋放oldpage相關信息
put_page(oldpage);//釋放頁
pte_unmap_unlock(ptep,ptl);
err= 0;
out:
returnerr;
}
unstable_tree_search_insert用來將一個條目比較並插入不穩定樹:
staticstructrmap_item *unstable_tree_search_insert(struct page *page,
structpage**page2,
structrmap_item*rmap_item)
{
structrb_node**new = &root_unstable_tree.rb_node;//不穩定樹
structrb_node *parent = NULL;
while(*new) {
structrmap_item*tree_rmap_item;
intret;
cond_resched();
tree_rmap_item=rb_entry(*new, struct rmap_item, node);
page2[0]= get_mergeable_page(tree_rmap_item);//取出不穩定樹的一個頁
if(!page2[0])
returnNULL;
/*
*Don't substitute an unswappable ksm page
*just for one good swappable forked page.
*/
if(page == page2[0]) {
put_page(page2[0]);
returnNULL;
}
ret= memcmp_pages(page, page2[0]);//比較頁面內容是否相同
parent= *new;
if(ret < 0) {
put_page(page2[0]);
new=&parent->rb_left;
}else if (ret > 0) {
put_page(page2[0]);
new=&parent->rb_right;
}else {
returntree_rmap_item;//找到同樣的頁面返回,後續合併入穩定樹
}
}
rmap_item->address|= NODE_FLAG;
rmap_item->address|=(ksm_scan.seqnr & SEQNR_MASK);
rb_link_node(&rmap_item->node,parent,new);//沒有找到相同的頁面,則插入到root_unstable_tree
rb_insert_color(&rmap_item->node,&root_unstable_tree);
ksm_pages_unshared++;
returnNULL;
}
KSM使用
KSM的管理和監控通過sysfs(位於根/sys/kernel/mm/ksm)執行。在這個sysfs子目錄中,有些用於開關、控制和監控。
#cat/sys/kernel/mm/ksm/
/sys/kernel/mm/ksm/full_scans /sys/kernel/mm/ksm/pages_unshared
/sys/kernel/mm/ksm/max_kernel_pages /sys/kernel/mm/ksm/pages_volatile
/sys/kernel/mm/ksm/pages_shared /sys/kernel/mm/ksm/run
/sys/kernel/mm/ksm/pages_sharing /sys/kernel/mm/ksm/sleep_millisecs
/sys/kernel/mm/ksm/pages_to_scan
開關:
Run文件用於啓用和禁用KSM的頁面合併。默認情況下,KSM被禁用(0),但可以通過將一個1寫入這個文件來啓用KSM守護進程(例如,echo1 > sys/kernel/mm/ksm/run)。通過寫入一個0,可以從運行狀態禁用這個守護進程(但是保留合併頁面的當前集合)。另外,通過寫入一個2,可以從運行狀態(1)停止KSM並請求取消合併所有合併頁面。
控制:
KSM運行時,可以通過其中3個參數(sysfs中的文件)來控制它。sleep_millisecs文件定義執行另一次頁面掃描前ksmd休眠的毫秒數默認20ms。最後,pages_to_scan文件定義一次給定掃描中可以掃描的頁面數,默認100。max_kernel_pages爲最多合併頁面數
監控:
還有5個通過sysfs導出的可監控文件(均爲只讀),它們表明ksmd的運行情況和效果。
full_scans:表明已經執行的全區域掃描的次數
pages_shared:stable穩定樹的節點數(共享後的物理頁面數)。
pages_sharing:表示被共享的物理頁面數。(例如將3個相同的頁面合併成1個頁面,則pages_shared=1,pages_sharing=2,兩者比例體現了頁面共享效率)
pages_unshared:ksm的暫未共享頁面數,即unstable不穩定樹的節點數。
ksm_rmap_items:反向映射條目數,可用來計算頻繁改變的頁面的數量。計算方式: ksm_pages_volatile=ksm_rmap_items
- ksm_pages_shared- ksm_pages_sharing -ksm_pages_unshared
運行前檢查配置項CONFIG_KSM是否配置
配置項:CONFIG_KSM
Symbol:KSM[=y]
Prompt:Enable KSM for page merging
Definedatmm/Kconfig:214
Dependson: MMU[=y]
Location:
->Kernel options
測試用例:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/mman.h>
#definePAGE_SIZE4096
#definePAGE_MASK(~(PAGE_SIZE - 1))
intmain(intargc, char **argv)
{
char*p;
intsize;
if(argc< 3)
gotofailed;
size=atoi(argv[1]) * 1024 * 1024;
if(!size)
gotofailed;
p=malloc(size);
if(!p)
gotofailed;
memset(p,atoi(argv[2]),size);
p=(char *)((unsigned long)p & PAGE_MASK);
size=madvise(p, size, 12);
if(size== -1)
perror("failed\n");
//scanf("%d",&size);
return0;
failed:
printf("failed\n");
return1;
}
測試過程結果:
#mount-n -t sysfs none /sys
#echo 1> /sys/kernel/mm/ksm/run
#cat/sys/kernel/mm/ksm/run
1
#free
total used free shared buffers
Mem: 2076068 11228 2064840 0 0
Swap: 0 0 0
Total: 2076068 11228 2064840
#./ksm200 3 & 200表示申請內存大小200M,3表示內存填充的值
#cat/sys/kernel/mm/ksm/pages_shared
1
#cat/sys/kernel/mm/ksm/pages_volatile
0
#cat/sys/kernel/mm/ksm/pages_sharing
51199
#cat/sys/kernel/mm/ksm/full_scans
3
#cat/sys/kernel/mm/ksm/full_scans
4
#cat/sys/kernel/mm/ksm/pages_volatile
0
#cat/sys/kernel/mm/ksm/pages_unshared
0
可見這場景下有效的節省了內存,申請200M物理頁面,實際只佔用了1個頁。
參考相關資料:
http://www.linux-kvm.com/content/using-ksm-kernel-samepage-merging-kvm
http://www.linux-kvm.org/page/KSM