你可以讓系統將特定的虛擬記憶體頁與實際頁幀相"關聯",並保持這樣的狀態(稱為
鎖定
)。該部分記憶體不會被swap機制交換出來,也不會產生pagefault(因為已經分配了實際的實體記憶體)。
為什麼需要鎖定記憶體
一個背景知識pagefault
使用者在分配出一部分虛擬記憶體時,其背後可能並沒有真正的實體記憶體與之對應,只有在使用者真正需要訪問記憶體時,系統才會為這段虛擬記憶體分配實際的實體記憶體,這個過程叫做pagefault
(缺頁異常)。這個過程對使用者來說是不感知的,所以使用者可以總是假定他要使用的虛擬記憶體背後有實際的實體記憶體
1. 速度
當使用者只是執行簡單的記憶體訪問時,pagefault
流程對使用者來說雖然是不感知的,耗時可以忽略不記,但是對於一些時間敏感型程式,尤其是實時程式,可能無法容忍執行速度的下降。
這種情況下,程式設計師可以先把所需要使用到的記憶體全部鎖定
,為它們提前分配好實際的實體記憶體,這樣在訪問時,就可以省去pagefault流程,提升程式執行速度。
2. 安全
如果你把一些祕密存放在虛擬記憶體中(比如使用者輸入的密碼),當虛擬記憶體被swap到磁碟後,就可能導致洩露。且可能在虛擬記憶體和實體記憶體被清除後很長一段時間依然存在。
副作用
當你每多lock一個頁幀,那麼可供其他虛擬記憶體使用的頁幀就少了一個,意味者系統裡可能發生更多的缺頁異常,更多的swap,而導致系統執行速度變慢。
一個極端的情況是,當你鎖定了所有的記憶體,系統將因為沒有實際可用的記憶體而無法執行
一些細節
1. 堆疊
記憶體鎖不會堆疊,你即使鎖定一段記憶體兩次,也只需要解鎖一次
2. 生命週期
記憶體鎖定會一直持續到擁有記憶體的程式顯示的解鎖它。但是程式終止
和exec
會導致虛擬記憶體不再存在,這可能意味著它不再被鎖定
3. 繼承
記憶體所不會被子程式繼承,(但請注意,在現代Unix系統中,在fork之後,父級和子級的虛擬地址空間由相同的實頁幀支援,因此子級享有父級的鎖)
4. 許可權
由於它能夠影響其他程式,因此只有超級使用者
可以鎖定,但所有程式都可以解鎖自己的記憶體
5. 寫入時複製的行為
這裡有一個非常有趣的行為,但我還沒有研究透,允許我先挖個坑
libc介面
mlock
將從addr
開始長度len
的記憶體鎖定
int mlock (const void *addr, size t len)
munlock
將從addr
開始長度len
的記憶體解鎖
int munlock (const void *addr, size t len)
mlockall
全部鎖定
int mlockall (int flags)
標誌位說明:
MCL_CURRENT
代表只鎖定當前已經分配的記憶體
MCL_FUTURE
將來分配的記憶體也會被立刻鎖定,注意單獨設定這個標誌位不會鎖定當前已經被分配的記憶體
注意 MCL_FUTURE
不會影響未來的程式地址空間,例如exec
後,該標誌位將被擦除
munlockall
沒啥好說的了,一次解鎖所有記憶體(自己程式的)
int munlockall (void)
參考文獻
The GNU C Library Reference Manual 3.5