苗 杰
國家計算機網絡與信息安全管理中心江蘇分中心
在一些業務的使用過程中,嚴格時間相關的程序,鎖住物理內存,可以避免內存頁頻繁調出調入帶來的性能損耗。對于安全性要求較高的應用程序,需要敏感數據被禁止輸出到交換文件中,否則程序結束后,攻擊者就可能從交換文件中恢復出這些敏感數據。Linux內核通過mlock的四個函數來實現物理內存頁的鎖定和解鎖,虛擬化環境中也可修改虛擬機的配置文件來啟用內存鎖定功能,從而滿足一些應用場景的需求。
mlock(memory locking)是Linux內核實現內存鎖定的一種方式,用來將進程使用的部分或者全部的虛擬內存鎖定到物理內存中。mlock機制主要有以下功能:(1)被鎖定的物理內存在被解鎖或進程退出前,不會被頁回收流程處理;(2)被鎖定的物理內存,不會被交換到swap設備;(3)進程執行mlock操作,立刻分配物理內存。
mlock機制提供以下四個函數供使用,如表1所示。

表1 mlock相關函數
每個進程都擁有一段連續的虛擬內存,內核實際的使用情況并不是以整個虛擬內存為管理單位,而是將整個虛擬內存劃分為若干個虛擬內存區域,內核中使用vm_area_struct數據結構來管理虛擬內存區域,簡稱vma。vma管理虛擬內存的方式如圖1所示。

圖1 vma管理虛擬內存
每一個vma代表一個已映射的、連續的且屬性相同(如可讀/寫)的虛擬內存區域。內核采用鏈表和AVL樹形式管理vma,鏈表用于遍歷,AVL樹用來查找。mlock操作會給相應的vma的vm_flags置一個VM_LOCKED標記,而這個標記則會影響到物理內存回收和交換。mlock鎖定的虛擬內存區域,可能跟現有的vma管理的虛擬內存區域并不完全重合,由于同一個vma的內存屬性要求一致,而是否具有VM_LOCKED標記也是其屬性之一,所以mlock操作可能導致現有的vma被合并或分割。vma的合并和分割如圖2所示。

圖2 vma的合并和分割
prev,next是已受鏈表管理的vma結構,new是將要新加入鏈表的vma。當new加入時,如果new的起始地址與prev的結束地址相同,且new屬性與prev屬性均為VM_LOCKED,則將prev和new合并成prev’。若new的結束地址與next的起始地址有重合,但next屬性是VM_EXEC,則next被分割成兩部分,一部分加入prev’,另一部分變成next’。
Linux給進程使用的虛擬內存由LRU來管理,操作系統對LRU的實現主要基于一對雙向鏈表,active_list和inactive_list。同時引入兩個頁面標志符PG_active和PG_referenced來標識某個頁面的活躍程度,從而決定如何在兩個鏈表之間移動頁面。
內核函數vmscan僅會遍歷掃描active_list和inactive_list鏈表來回收頁面。內核在LRU中新增了一個unevictable_list,將不可回收的頁面都放在unevictable_list中,mlock的頁被放在unevictable_list中,同時給該頁置一個PG_mlocked標記。除了mlock的頁,ramdisk或ramfs的頁和共享內存映射的頁也被放入unevictable_list中。
解鎖并不立刻將解鎖的頁回收,而是將解鎖的頁放回active_list或inactive_list鏈表,然后交由頁回收流程處理,所以mlock的頁不會被頁回收流程處理。
當通過fork系統調用創建一個子進程,子進程將拷貝父進程的整個虛擬內存,包括對所有vma的拷貝,不過子進程的vma并不繼承VM_LOCKED標記。由于線程共享進程資源,所以線程的vma將繼承VM_LOCKED標記。
從RHEL6.5GA版本中,Linux系統虛擬化libvirt已提供對mlock功能的支持。當啟動虛擬機時,默認情況下qemukvm進程中的mlock功能是關閉的。若要開啟qemu-kvm進程的mlock功能,需在虛擬機的xml文件中,添加如下配置行:

網卡設備以PCI/Virtio方式添加到虛擬機時,給虛擬機配置10GB的內存,經測試虛擬機啟動后實際占用的物理內存如下表2所示:

表2 mlock功能對虛擬機內存的影響
進程一般會訪問虛擬內存,需要建立虛擬內存和物理內存的映射關系,而啟用mlock功能會對進程運行中的page fault產生影響,進而影響內存訪問時間。mlock功能對內存訪問的影響如表3所示。

表3 mlock功能對內存訪問的影響
(1)采用mlock機制,僅在進程虛擬內存和物理內存建立映射關系時產生一次page fault。
(2)未采用mlock機制,每當訪問進程中不同的虛擬內存單元時,就會產生一次page fault。
若進程中涉及實時處理環節,應在進入實時處理環節前,將進程虛擬內存鎖定到物理內存,否則page fault會影響實時處理。
在一些實際應用中會涉及多線程操作,每個線程均有自己的棧空間,啟用mlock功能會對線程棧空間映射物理內存帶來一定的影響,如表4所示。

表4 mlock功能對線程映射的影響

映射物理內存時間開銷off 1MB 低mlock=on|off 線程棧空間映射物理內存大小
假設線程整個棧空間大小10MB,其中1MB空間已使用。假設10MB虛擬內存和物理內存建立映射關系耗時1ms,1MB虛擬內存和物理內存建立映射關系耗時0.1ms。當進程涉及多線程操作時:
(1)采用mlock機制,線程整個棧空間10MB會被映射到物理內存,若一個進程中有2000個線程,則線程棧空間和物理內存建立映射關系共耗時2000ms。
(2)未采用mlock機制,線程棧空間已使用的1MB會被映射到物理內存,若一個進程中有2000個線程,則線程棧空間和物理內存建立映射關系共耗時200ms。
若進程涉及多線程操作,采用mlock機制,線程棧空間和物理內存建立映射關系將帶來額外的時間開銷。
為了驗證mlock功能對業務性能帶來的影響,按如下兩種應用場景分別對其進行性能測試。
(1)利用進程測試工具,驗證mlock功能是否開啟對其創建線程性能的影響。測試結果如表5所示。

表5 mlock功能對創建線程性能影響

圖3 mlock功能對創建線程性能影響
測試單位為thread/s;

從表5中可以看出隨著并發進程逐漸增多,使用mlock的MCL_FUTURE特性,會嚴重影響進程創建線程的性能。圖形效果如圖3所示。
(2)動態分配內存20GB,驗證mlock功能是否開啟對page fault產生的影響。如表6所示。

表6 mlock功能對page fault的影響
從表6中可以看出使用mlock的MCL_FUTURE特性,動態分配內存時會大幅增加page fault,圖形效果如圖4所示。

圖4 mlock功能對page fault影響
基于Linux系統對mlock實現原理進行了研究,并測試了mlock功能開啟對進程創建線程和page fault產生的性能影響。從兩個測試結果可以看出,使用mlock的MCL_FUTURE特性來進行內存鎖定,當進程涉及動態內存分配時會大幅增加page fault,同時并發進程數增加會嚴重降低進程創建線程的性能,這是由mlock實時鎖定了物理內存造成的。因此可以根據具體情況和需求來決定是否需要使用mlock功能。