你可以讓系統將特定的虛擬內存頁與實際頁幀相"關聯",並保持這樣的狀態(稱為
鎖定
)。該部分內存不會被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