轉載:KSM (內存管理合並相同頁)

轉載自:http://blog.csdn.net/haitaoliang/article/details/25003395


Ksm介紹

2.6.32引入了KSM(KernelSamepage Merging)允許這個系統管理程序通過合併內存頁面來增加併發虛擬機的數量。VMwareESX服務器系統管理程序將這個特性命名爲TransparentPage Sharing (TPS),而XEN將其稱爲MemoryCoW。不管採用哪種名稱和實現,這個特性都提供了更好的內存利用率,從而允許操作系統(KVM的系統管理程序)過量使用內存,支持更多的應用程序或VM

假如操作系統和應用程序代碼以及常量數據在VMs之間相同,那麼這個特點就很有用。當頁面惟一時,它們可以被合併,從而釋放內存,供其他應用程序使用。

一臺主機(Host)同時運行好幾個相同類型的實例(guests),通過這種技術共享相同代碼,比如每個guest的核心代碼,那麼隨着guest實例增加,Host內存不會急劇的下降,有效的增加Hostprovisioning能力。同時釋放出內存,供其他系統或程序使用。這也是我們如何能夠讓一臺16G內存的Server跑起521G內存XP系統的方法。VMwareESX/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文件定義一次給定掃描中可以掃描的頁面數,默認100max_kernel_pages爲最多合併頁面數

     監控:

還有5個通過sysfs導出的可監控文件(均爲只讀),它們表明ksmd的運行情況和效果。

full_scans:表明已經執行的全區域掃描的次數

pages_sharedstable穩定樹的節點數(共享後的物理頁面數)。 
pages_sharing
:表示被共享的物理頁面數。(例如將3個相同的頁面合併成1個頁面,則pages_shared=1pages_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表示申請內存大小200M3表示內存填充的值

#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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章