內(nèi)存鎖定
linux實(shí)現(xiàn)了請(qǐng)求頁面調(diào)度(在需要時(shí)將頁面從硬盤交換進(jìn)來汉嗽,當(dāng)不再需要時(shí)再交換出去)呀闻,這使得系統(tǒng)中進(jìn)程的虛擬地址空間與實(shí)際的物理內(nèi)存大小沒有直接的關(guān)系厨相。
交換對(duì)進(jìn)程來說是透明的炼七,應(yīng)用程序一般都不需要關(guān)心內(nèi)核頁面調(diào)度的行為。然而在下面兩種情況下虱朵,應(yīng)用程序可能希望影響系統(tǒng)的頁面調(diào)度:
- 確定性:時(shí)間約束嚴(yán)格的應(yīng)用程序需要自己來決定頁的調(diào)度行為莉炉。
- 安全性:如果內(nèi)存中含有私人信息,這些信息可能最終被頁面調(diào)度以不加密的方式儲(chǔ)存到硬盤上碴犬。在一個(gè)高度注重安全性的環(huán)境中絮宁,這樣做可能是不可接收的。這樣的應(yīng)用程序可以請(qǐng)求將密鑰一直保留在物理內(nèi)存上翅敌。
- 注意:雖然內(nèi)核提供了內(nèi)存鎖定的功能供應(yīng)用程序在需要的時(shí)候使用羞福,但是改變內(nèi)核的行為可能會(huì)對(duì)系統(tǒng)整體的表現(xiàn)產(chǎn)生負(fù)面的影響惕蹄。應(yīng)用的確定性和安全性可能會(huì)提高蚯涮,但是當(dāng)它的頁被鎖在了內(nèi)存中治专,那另一個(gè)應(yīng)用的頁就只能被換出內(nèi)存。所以遭顶,我們應(yīng)該有選擇的去使用內(nèi)存鎖定张峰,而不能盲目地使用。
鎖定部分地址空間
POSIX1003.1b-1993定義兩個(gè)接口將一個(gè)或多個(gè)頁面“鎖定”在物理內(nèi)存棒旗,來保證它們不會(huì)被交換到磁盤喘批。
-
mlock( ):
mlock( )鎖定給定的一個(gè)地址空間:
#include <sys/mman.h>
int mlock(const void *addr, size_t len);
調(diào)用mlock( )將鎖定addr開始長度為len個(gè)字節(jié)的虛擬內(nèi)存。成功時(shí)函數(shù)返回0,失敗返回-1,并適當(dāng)設(shè)置errno铣揉。
- 成功調(diào)用會(huì)將所有包含[addr, addr+len)的物理內(nèi)存頁鎖定饶深。(例如,一個(gè)調(diào)用只是指定了一個(gè)字節(jié)逛拱,包含這個(gè)字節(jié)的所有物理內(nèi)存頁都將被鎖定)敌厘。
- POSIX標(biāo)準(zhǔn)要求addr應(yīng)該與頁邊界對(duì)齊。Linux沒有強(qiáng)制要求朽合,如果真要這樣做的時(shí)候俱两,會(huì)悄悄的將addr向下調(diào)整到最近的頁面。
- 一個(gè)由fork( )產(chǎn)生的子進(jìn)程并不從父進(jìn)程處繼承鎖定的內(nèi)存曹步。然而宪彩,由于Linux對(duì)地址空間COW機(jī)制,子進(jìn)程的頁面被鎖定在內(nèi)存中直到子進(jìn)程對(duì)它們執(zhí)行寫操作讲婚。
-
mlockall( ):
如果一個(gè)進(jìn)程想在物理內(nèi)存中鎖定它的全部地址空間尿孔,可以使用mlockall( ):
#include <sys/mman.h>
int mlockall(int flags);
mlockall( )函數(shù)鎖定一個(gè)進(jìn)程現(xiàn)有的地址空間在物理內(nèi)存中的所有頁面。
flags參數(shù)筹麸,是下面兩個(gè)值的按位或操作纳猫,用以控制函數(shù)行為:(大部分應(yīng)用程序會(huì)同時(shí)設(shè)定這兩個(gè)值)
- MCL_CURRENT: 如果設(shè)置了該值,mlockall( )會(huì)將所有已被映射的頁面(包括棧竹捉,數(shù)據(jù)段芜辕,映射文件)鎖定進(jìn)程地址空間中。
- MCL_FUTURE: 如果設(shè)置了該值块差,mlockall( )會(huì)將所有未來映射的頁面也鎖定到進(jìn)程地址空間中侵续。
內(nèi)存解鎖
POSIX標(biāo)準(zhǔn)提供了兩個(gè)接口用來將頁從內(nèi)存中解鎖,允許內(nèi)核根據(jù)需要將頁換出至硬盤中憨闰。
#include <sys/mman.h>
int munlock(const void *addr, size_t len);
in munlockall(void);
- munlock( )解除addr開始長為len的內(nèi)存所在的頁面地鎖定状蜗,它消除mlock( )的效果.
- munlockall( )消除mlockall( )的效果.
兩個(gè)函數(shù)在成功時(shí)都返回0,失敗時(shí)返回-1.
內(nèi)存鎖定并不會(huì)重疊鹉动,所以不管mlock( )或mlockall( )了幾次轧坎,僅一個(gè)munlock( )或munlockall( )會(huì)解除一個(gè)頁面的鎖定。
linux對(duì)于一個(gè)進(jìn)程能鎖定的頁面數(shù)進(jìn)行了限制:擁有CAP_IPC_LOCK權(quán)限的進(jìn)程能鎖定任意多的頁面泽示。沒有這個(gè)權(quán)限的進(jìn)程只能鎖定RLIMIT_MEMLOCK個(gè)字節(jié)缸血,默認(rèn)情況下蜜氨,該限制是32KB。
判斷一個(gè)頁面在不在物理內(nèi)存中
mincore( )函數(shù)捎泻,用來確定一個(gè)給定范圍的內(nèi)存是在物理內(nèi)存中還是被交換到了硬盤中:
#include <unistd.h>
#include <sys/mman.h>
int mincore(void *start, size_t length, unsigned char *vec);
函數(shù)通過vec來返回向量飒炎,這個(gè)向量描述start(必須頁面對(duì)齊)開始長為length(不需要對(duì)齊)字節(jié)的內(nèi)存中的頁面的情況。
- vec的每個(gè)字節(jié)對(duì)應(yīng)指定區(qū)域內(nèi)的一個(gè)頁面笆豁,第一個(gè)字節(jié)對(duì)應(yīng)著第一個(gè)頁面郎汪,然后依次對(duì)應(yīng)。
- vec必須足夠大來裝入(length - 1 + page_size)/page_size字節(jié)闯狱。如果頁面在物理內(nèi)存中煞赢,對(duì)應(yīng)字節(jié)的最低位是1,否則是0。其他的位目前還沒有定義哄孤。
- 目前來說耕驰,這個(gè)系統(tǒng)調(diào)用只能用在以MAP_SHARED創(chuàng)建的基于文件的映射上。
投機(jī)性存儲(chǔ)分配策略
Linux使用投機(jī)性分配策略:當(dāng)一個(gè)進(jìn)程向內(nèi)核請(qǐng)求額外的內(nèi)存-如擴(kuò)大它的數(shù)據(jù)段录豺,或者創(chuàng)建一個(gè)新的存儲(chǔ)器映射-內(nèi)核作出了分配承諾但實(shí)際上并沒有分給進(jìn)程任何的物理存儲(chǔ)朦肘。
- 僅當(dāng)進(jìn)程對(duì)新“分配到”的內(nèi)存區(qū)域作寫操作的時(shí)候,內(nèi)核才履行承諾双饥,分配一塊物理內(nèi)存媒抠。內(nèi)核逐頁完成上述工作,并在需要時(shí)進(jìn)行請(qǐng)求頁面調(diào)度和寫時(shí)復(fù)制咏花。
這樣處理有如下幾個(gè)優(yōu)點(diǎn):
- 延緩內(nèi)存分配允許內(nèi)核將大部分工作推遲到最后一刻(當(dāng)確實(shí)需要進(jìn)行分配時(shí))
- 由于請(qǐng)求是根據(jù)需求逐頁的分配趴生,只有真正需要物理內(nèi)存的時(shí)候才會(huì)消耗物理存儲(chǔ)
- 分配到的內(nèi)存可能比實(shí)際的物理內(nèi)存甚至比可用的交換空間多的多,這個(gè)特征叫超量使用昏翰。
超量使用和內(nèi)存耗盡
超量使用的好處:和在應(yīng)用請(qǐng)求頁面就分配物理存儲(chǔ)相比苍匆,在使用時(shí)刻才分配物理存儲(chǔ)的過量使用機(jī)制允許系統(tǒng)運(yùn)行更多,更大的應(yīng)用程序棚菊。
但是浸踩,如果系統(tǒng)中的進(jìn)程為滿足超量使用而申請(qǐng)的內(nèi)存大于物理內(nèi)存和交換空間之和,內(nèi)核只能殺死另一個(gè)進(jìn)程并釋放它的內(nèi)存统求,以此來滿足下一次的分配需求检碗。
- 當(dāng)超量使用導(dǎo)致內(nèi)存不足以滿足一個(gè)請(qǐng)求時(shí),就發(fā)生了內(nèi)存耗盡(OOM)(out of memory)码邻。
- 為了處理OOM折剃,內(nèi)核使用OOM終結(jié)者killer來挑選一個(gè)進(jìn)程,并終止它像屋。基于這個(gè)目的,內(nèi)核會(huì)嘗試選出一個(gè)最不重要且又占用很多內(nèi)存的進(jìn)程戈轿。
關(guān)閉超量使用:
內(nèi)核允許通過文件/proc/sys/vm/overcommit_memory關(guān)閉超量使用扶檐,和此功能相似的還有sysctl的vm.overcommit_memory參數(shù)凶杖。
- 參數(shù)默認(rèn)值為0,告訴內(nèi)核執(zhí)行適度的超量使用策略
- 參數(shù)值為1時(shí)胁艰,確認(rèn)所有的分配請(qǐng)求
- 參數(shù)值為2時(shí)款筑,關(guān)閉所有的過量使用腾么,啟用嚴(yán)格審計(jì)(strict accounting)策略.
嚴(yán)格審計(jì)策略:
在嚴(yán)格審計(jì)模式中,承諾的內(nèi)存大小被嚴(yán)格限制在交換空間的大小加上可調(diào)比例的物理內(nèi)存大小解虱。
- 比例默認(rèn)為50%攘须,因?yàn)槲锢韮?nèi)存還必須包含著內(nèi)核,頁表殴泰,系統(tǒng)保留頁于宙,鎖定頁等等東西悍汛。僅它的一部分能夠被交換和滿足承諾請(qǐng)求。
- 比例可以在文件/proc/sys/vm/overcommit_ratio里面設(shè)置谱俭,作用和vm.overcommit_ratio的sysctl參數(shù)相似宵蛀。
使用嚴(yán)格審計(jì)策略時(shí)要非常小心!許多系統(tǒng)設(shè)計(jì)者認(rèn)為嚴(yán)格審計(jì)策略才是解決之道术陶,然而,應(yīng)用程序常常進(jìn)行一些不必要的梧宫、且只有使用超量使用才能滿足的分配請(qǐng)求,而允許這種行為也是設(shè)計(jì)虛擬內(nèi)存的主要?jiǎng)訖C(jī)之一疤坝。