苗 杰
國家計(jì)算機(jī)網(wǎng)絡(luò)與信息安全管理中心江蘇分中心
在一些業(yè)務(wù)的使用過程中,嚴(yán)格時間相關(guān)的程序,鎖住物理內(nèi)存,可以避免內(nèi)存頁頻繁調(diào)出調(diào)入帶來的性能損耗。對于安全性要求較高的應(yīng)用程序,需要敏感數(shù)據(jù)被禁止輸出到交換文件中,否則程序結(jié)束后,攻擊者就可能從交換文件中恢復(fù)出這些敏感數(shù)據(jù)。Linux內(nèi)核通過mlock的四個函數(shù)來實(shí)現(xiàn)物理內(nèi)存頁的鎖定和解鎖,虛擬化環(huán)境中也可修改虛擬機(jī)的配置文件來啟用內(nèi)存鎖定功能,從而滿足一些應(yīng)用場景的需求。
mlock(memory locking)是Linux內(nèi)核實(shí)現(xiàn)內(nèi)存鎖定的一種方式,用來將進(jìn)程使用的部分或者全部的虛擬內(nèi)存鎖定到物理內(nèi)存中。mlock機(jī)制主要有以下功能:(1)被鎖定的物理內(nèi)存在被解鎖或進(jìn)程退出前,不會被頁回收流程處理;(2)被鎖定的物理內(nèi)存,不會被交換到swap設(shè)備;(3)進(jìn)程執(zhí)行mlock操作,立刻分配物理內(nèi)存。
mlock機(jī)制提供以下四個函數(shù)供使用,如表1所示。
表1 mlock相關(guān)函數(shù)
每個進(jìn)程都擁有一段連續(xù)的虛擬內(nèi)存,內(nèi)核實(shí)際的使用情況并不是以整個虛擬內(nèi)存為管理單位,而是將整個虛擬內(nèi)存劃分為若干個虛擬內(nèi)存區(qū)域,內(nèi)核中使用vm_area_struct數(shù)據(jù)結(jié)構(gòu)來管理虛擬內(nèi)存區(qū)域,簡稱vma。vma管理虛擬內(nèi)存的方式如圖1所示。
圖1 vma管理虛擬內(nèi)存
每一個vma代表一個已映射的、連續(xù)的且屬性相同(如可讀/寫)的虛擬內(nèi)存區(qū)域。內(nèi)核采用鏈表和AVL樹形式管理vma,鏈表用于遍歷,AVL樹用來查找。mlock操作會給相應(yīng)的vma的vm_flags置一個VM_LOCKED標(biāo)記,而這個標(biāo)記則會影響到物理內(nèi)存回收和交換。mlock鎖定的虛擬內(nèi)存區(qū)域,可能跟現(xiàn)有的vma管理的虛擬內(nèi)存區(qū)域并不完全重合,由于同一個vma的內(nèi)存屬性要求一致,而是否具有VM_LOCKED標(biāo)記也是其屬性之一,所以mlock操作可能導(dǎo)致現(xiàn)有的vma被合并或分割。vma的合并和分割如圖2所示。
圖2 vma的合并和分割
prev,next是已受鏈表管理的vma結(jié)構(gòu),new是將要新加入鏈表的vma。當(dāng)new加入時,如果new的起始地址與prev的結(jié)束地址相同,且new屬性與prev屬性均為VM_LOCKED,則將prev和new合并成prev’。若new的結(jié)束地址與next的起始地址有重合,但next屬性是VM_EXEC,則next被分割成兩部分,一部分加入prev’,另一部分變成next’。
Linux給進(jìn)程使用的虛擬內(nèi)存由LRU來管理,操作系統(tǒng)對LRU的實(shí)現(xiàn)主要基于一對雙向鏈表,active_list和inactive_list。同時引入兩個頁面標(biāo)志符PG_active和PG_referenced來標(biāo)識某個頁面的活躍程度,從而決定如何在兩個鏈表之間移動頁面。
內(nèi)核函數(shù)vmscan僅會遍歷掃描active_list和inactive_list鏈表來回收頁面。內(nèi)核在LRU中新增了一個unevictable_list,將不可回收的頁面都放在unevictable_list中,mlock的頁被放在unevictable_list中,同時給該頁置一個PG_mlocked標(biāo)記。除了mlock的頁,ramdisk或ramfs的頁和共享內(nèi)存映射的頁也被放入unevictable_list中。
解鎖并不立刻將解鎖的頁回收,而是將解鎖的頁放回active_list或inactive_list鏈表,然后交由頁回收流程處理,所以mlock的頁不會被頁回收流程處理。
當(dāng)通過fork系統(tǒng)調(diào)用創(chuàng)建一個子進(jìn)程,子進(jìn)程將拷貝父進(jìn)程的整個虛擬內(nèi)存,包括對所有vma的拷貝,不過子進(jìn)程的vma并不繼承VM_LOCKED標(biāo)記。由于線程共享進(jìn)程資源,所以線程的vma將繼承VM_LOCKED標(biāo)記。
從RHEL6.5GA版本中,Linux系統(tǒng)虛擬化libvirt已提供對mlock功能的支持。當(dāng)啟動虛擬機(jī)時,默認(rèn)情況下qemukvm進(jìn)程中的mlock功能是關(guān)閉的。若要開啟qemu-kvm進(jìn)程的mlock功能,需在虛擬機(jī)的xml文件中,添加如下配置行:
網(wǎng)卡設(shè)備以PCI/Virtio方式添加到虛擬機(jī)時,給虛擬機(jī)配置10GB的內(nèi)存,經(jīng)測試虛擬機(jī)啟動后實(shí)際占用的物理內(nèi)存如下表2所示:
表2 mlock功能對虛擬機(jī)內(nèi)存的影響
進(jìn)程一般會訪問虛擬內(nèi)存,需要建立虛擬內(nèi)存和物理內(nèi)存的映射關(guān)系,而啟用mlock功能會對進(jìn)程運(yùn)行中的page fault產(chǎn)生影響,進(jìn)而影響內(nèi)存訪問時間。mlock功能對內(nèi)存訪問的影響如表3所示。
表3 mlock功能對內(nèi)存訪問的影響
(1)采用mlock機(jī)制,僅在進(jìn)程虛擬內(nèi)存和物理內(nèi)存建立映射關(guān)系時產(chǎn)生一次page fault。
(2)未采用mlock機(jī)制,每當(dāng)訪問進(jìn)程中不同的虛擬內(nèi)存單元時,就會產(chǎn)生一次page fault。
若進(jìn)程中涉及實(shí)時處理環(huán)節(jié),應(yīng)在進(jìn)入實(shí)時處理環(huán)節(jié)前,將進(jìn)程虛擬內(nèi)存鎖定到物理內(nèi)存,否則page fault會影響實(shí)時處理。
在一些實(shí)際應(yīng)用中會涉及多線程操作,每個線程均有自己的??臻g,啟用mlock功能會對線程??臻g映射物理內(nèi)存帶來一定的影響,如表4所示。
表4 mlock功能對線程映射的影響
映射物理內(nèi)存時間開銷off 1MB 低mlock=on|off 線程棧空間映射物理內(nèi)存大小
假設(shè)線程整個??臻g大小10MB,其中1MB空間已使用。假設(shè)10MB虛擬內(nèi)存和物理內(nèi)存建立映射關(guān)系耗時1ms,1MB虛擬內(nèi)存和物理內(nèi)存建立映射關(guān)系耗時0.1ms。當(dāng)進(jìn)程涉及多線程操作時:
(1)采用mlock機(jī)制,線程整個??臻g10MB會被映射到物理內(nèi)存,若一個進(jìn)程中有2000個線程,則線程??臻g和物理內(nèi)存建立映射關(guān)系共耗時2000ms。
(2)未采用mlock機(jī)制,線程棧空間已使用的1MB會被映射到物理內(nèi)存,若一個進(jìn)程中有2000個線程,則線程??臻g和物理內(nèi)存建立映射關(guān)系共耗時200ms。
若進(jìn)程涉及多線程操作,采用mlock機(jī)制,線程棧空間和物理內(nèi)存建立映射關(guān)系將帶來額外的時間開銷。
為了驗(yàn)證mlock功能對業(yè)務(wù)性能帶來的影響,按如下兩種應(yīng)用場景分別對其進(jìn)行性能測試。
(1)利用進(jìn)程測試工具,驗(yàn)證mlock功能是否開啟對其創(chuàng)建線程性能的影響。測試結(jié)果如表5所示。
表5 mlock功能對創(chuàng)建線程性能影響
圖3 mlock功能對創(chuàng)建線程性能影響
測試單位為thread/s;
從表5中可以看出隨著并發(fā)進(jìn)程逐漸增多,使用mlock的MCL_FUTURE特性,會嚴(yán)重影響進(jìn)程創(chuàng)建線程的性能。圖形效果如圖3所示。
(2)動態(tài)分配內(nèi)存20GB,驗(yàn)證mlock功能是否開啟對page fault產(chǎn)生的影響。如表6所示。
表6 mlock功能對page fault的影響
從表6中可以看出使用mlock的MCL_FUTURE特性,動態(tài)分配內(nèi)存時會大幅增加page fault,圖形效果如圖4所示。
圖4 mlock功能對page fault影響
基于Linux系統(tǒng)對mlock實(shí)現(xiàn)原理進(jìn)行了研究,并測試了mlock功能開啟對進(jìn)程創(chuàng)建線程和page fault產(chǎn)生的性能影響。從兩個測試結(jié)果可以看出,使用mlock的MCL_FUTURE特性來進(jìn)行內(nèi)存鎖定,當(dāng)進(jìn)程涉及動態(tài)內(nèi)存分配時會大幅增加page fault,同時并發(fā)進(jìn)程數(shù)增加會嚴(yán)重降低進(jìn)程創(chuàng)建線程的性能,這是由mlock實(shí)時鎖定了物理內(nèi)存造成的。因此可以根據(jù)具體情況和需求來決定是否需要使用mlock功能。