前言
在Linux系統(tǒng)中,我們經(jīng)常用free命令來(lái)查看系統(tǒng)內(nèi)存的使用狀態(tài)。在一個(gè)RHEL6的系統(tǒng)上,free命令的顯示內(nèi)容大概是這樣一個(gè)狀態(tài):
?
[root@tencent64?~]#?free ?????????????total???????used???????free?????shared????buffers?????cached Mem:?????132256952???72571772???59685180??????????0????1762632???53034704 -/+?buffers/cache:???17774436??114482516 Swap:??????2101192????????508????2100684
?
這里的默認(rèn)顯示單位是kb,我的服務(wù)器是128G內(nèi)存,所以數(shù)字顯得比較大。這個(gè)命令幾乎是每一個(gè)使用過(guò)Linux的人必會(huì)的命令,但越是這樣的命令,似乎真正明白的人越少(我是說(shuō)比例越少)。一般情況下,對(duì)此命令輸出的理解可以分這幾個(gè)層次:
不了解。這樣的人的第一反應(yīng)是:天啊,內(nèi)存用了好多,70個(gè)多G,可是我?guī)缀鯖](méi)有運(yùn)行什么大程序???為什么會(huì)這樣?Linux好占內(nèi)存!
自以為很了解。這樣的人一般自習(xí)評(píng)估過(guò)會(huì)說(shuō):嗯,根據(jù)我專業(yè)的眼光看出來(lái),內(nèi)存才用了17G左右,還有很多剩余內(nèi)存可用。buffers/cache占用的較多,說(shuō)明系統(tǒng)中有進(jìn)程曾經(jīng)讀寫(xiě)過(guò)文件,但是不要緊,這部分內(nèi)存是當(dāng)空閑來(lái)用的。
真的很了解。這種人的反應(yīng)反而讓人感覺(jué)最不懂Linux,他們的反應(yīng)是:free顯示的是這樣,好吧我知道了。神馬?你問(wèn)我這些內(nèi)存夠不夠,我當(dāng)然不知道啦!我特么怎么知道你程序怎么寫(xiě)的?
根據(jù)目前網(wǎng)絡(luò)上技術(shù)文檔的內(nèi)容,我相信絕大多數(shù)了解一點(diǎn)Linux的人應(yīng)該處在第二種層次。大家普遍認(rèn)為,buffers和cached所占用的內(nèi)存空間是可以在內(nèi)存壓力較大的時(shí)候被釋放當(dāng)做空閑空間用的。但真的是這樣么?在論證這個(gè)題目之前,我們先簡(jiǎn)要介紹一下buffers和cached是什么意思:
什么是buffer/cache?
buffer和cache是兩個(gè)在計(jì)算機(jī)技術(shù)中被用濫的名詞,放在不通語(yǔ)境下會(huì)有不同的意義。在Linux的內(nèi)存管理中,這里的buffer指Linux內(nèi)存的:Buffer cache。這里的cache指Linux內(nèi)存中的:Page cache。翻譯成中文可以叫做緩沖區(qū)緩存和頁(yè)面緩存。在歷史上,它們一個(gè)(buffer)被用來(lái)當(dāng)成對(duì)io設(shè)備寫(xiě)的緩存,而另一個(gè)(cache)被用來(lái)當(dāng)作對(duì)io設(shè)備的讀緩存,這里的io設(shè)備,主要指的是塊設(shè)備文件和文件系統(tǒng)上的普通文件。但是現(xiàn)在,它們的意義已經(jīng)不一樣了。在當(dāng)前的內(nèi)核中,page cache顧名思義就是針對(duì)內(nèi)存頁(yè)的緩存,說(shuō)白了就是,如果有內(nèi)存是以page進(jìn)行分配管理的,都可以使用page cache作為其緩存來(lái)管理使用。當(dāng)然,不是所有的內(nèi)存都是以頁(yè)(page)進(jìn)行管理的,也有很多是針對(duì)塊(block)進(jìn)行管理的,這部分內(nèi)存使用如果要用到cache功能,則都集中到buffer cache中來(lái)使用。(從這個(gè)角度出發(fā),是不是buffer cache改名叫做block cache更好?)然而,也不是所有塊(block)都有固定長(zhǎng)度,系統(tǒng)上塊的長(zhǎng)度主要是根據(jù)所使用的塊設(shè)備決定的,而頁(yè)長(zhǎng)度在X86上無(wú)論是32位還是64位都是4k。
明白了這兩套緩存系統(tǒng)的區(qū)別,就可以理解它們究竟都可以用來(lái)做什么了。
什么是page cache
Page cache主要用來(lái)作為文件系統(tǒng)上的文件數(shù)據(jù)的緩存來(lái)用,尤其是針對(duì)當(dāng)進(jìn)程對(duì)文件有read/write操作的時(shí)候。如果你仔細(xì)想想的話,作為可以映射文件到內(nèi)存的系統(tǒng)調(diào)用:mmap是不是很自然的也應(yīng)該用到page cache?在當(dāng)前的系統(tǒng)實(shí)現(xiàn)里,page cache也被作為其它文件類型的緩存設(shè)備來(lái)用,所以事實(shí)上page cache也負(fù)責(zé)了大部分的塊設(shè)備文件的緩存工作。
什么是buffer cache
Buffer cache則主要是設(shè)計(jì)用來(lái)在系統(tǒng)對(duì)塊設(shè)備進(jìn)行讀寫(xiě)的時(shí)候,對(duì)塊進(jìn)行數(shù)據(jù)緩存的系統(tǒng)來(lái)使用。這意味著某些對(duì)塊的操作會(huì)使用buffer cache進(jìn)行緩存,比如我們?cè)诟袷交募到y(tǒng)的時(shí)候。一般情況下兩個(gè)緩存系統(tǒng)是一起配合使用的,比如當(dāng)我們對(duì)一個(gè)文件進(jìn)行寫(xiě)操作的時(shí)候,page cache的內(nèi)容會(huì)被改變,而buffer cache則可以用來(lái)將page標(biāo)記為不同的緩沖區(qū),并記錄是哪一個(gè)緩沖區(qū)被修改了。這樣,內(nèi)核在后續(xù)執(zhí)行臟數(shù)據(jù)的回寫(xiě)(writeback)時(shí),就不用將整個(gè)page寫(xiě)回,而只需要寫(xiě)回修改的部分即可。
如何回收cache?
Linux內(nèi)核會(huì)在內(nèi)存將要耗盡的時(shí)候,觸發(fā)內(nèi)存回收的工作,以便釋放出內(nèi)存給急需內(nèi)存的進(jìn)程使用。一般情況下,這個(gè)操作中主要的內(nèi)存釋放都來(lái)自于對(duì)buffer/cache的釋放。尤其是被使用更多的cache空間。既然它主要用來(lái)做緩存,只是在內(nèi)存夠用的時(shí)候加快進(jìn)程對(duì)文件的讀寫(xiě)速度,那么在內(nèi)存壓力較大的情況下,當(dāng)然有必要清空釋放cache,作為free空間分給相關(guān)進(jìn)程使用。所以一般情況下,我們認(rèn)為buffer/cache空間可以被釋放,這個(gè)理解是正確的。
但是這種清緩存的工作也并不是沒(méi)有成本。理解cache是干什么的就可以明白清緩存必須保證cache中的數(shù)據(jù)跟對(duì)應(yīng)文件中的數(shù)據(jù)一致,才能對(duì)cache進(jìn)行釋放。所以伴隨著cache清除的行為的,一般都是系統(tǒng)IO飆高。因?yàn)閮?nèi)核要對(duì)比cache中的數(shù)據(jù)和對(duì)應(yīng)硬盤(pán)文件上的數(shù)據(jù)是否一致,如果不一致需要寫(xiě)回,之后才能回收。
在系統(tǒng)中除了內(nèi)存將被耗盡的時(shí)候可以清緩存以外,我們還可以使用下面這個(gè)文件來(lái)人工觸發(fā)緩存清除的操作:
?
[root@tencent64?~]#?cat?/proc/sys/vm/drop_caches? 1
?
方法是:
?
echo?1?>?/proc/sys/vm/drop_caches
?
當(dāng)然,這個(gè)文件可以設(shè)置的值分別為1、2、3。它們所表示的含義為:echo 1 > /proc/sys/vm/drop_caches:表示清除pagecache。
echo 2 > /proc/sys/vm/drop_caches:表示清除回收slab分配器中的對(duì)象(包括目錄項(xiàng)緩存和inode緩存)。slab分配器是內(nèi)核中管理內(nèi)存的一種機(jī)制,其中很多緩存數(shù)據(jù)實(shí)現(xiàn)都是用的pagecache。
echo 3 > /proc/sys/vm/drop_caches:表示清除pagecache和slab分配器中的緩存對(duì)象。
cache都能被回收么?
我們分析了cache能被回收的情況,那么有沒(méi)有不能被回收的cache呢?當(dāng)然有。我們先來(lái)看第一種情況:
tmpfs
大家知道Linux提供一種“臨時(shí)”文件系統(tǒng)叫做tmpfs,它可以將內(nèi)存的一部分空間拿來(lái)當(dāng)做文件系統(tǒng)使用,使內(nèi)存空間可以當(dāng)做目錄文件來(lái)用?,F(xiàn)在絕大多數(shù)Linux系統(tǒng)都有一個(gè)叫做/dev/shm的tmpfs目錄,就是這樣一種存在。當(dāng)然,我們也可以手工創(chuàng)建一個(gè)自己的tmpfs,方法如下:
?
[root@tencent64?~]#?mkdir?/tmp/tmpfs [root@tencent64?~]#?mount?-t?tmpfs?-o?size=20G?none?/tmp/tmpfs/ [root@tencent64?~]#?df Filesystem???????????1K-blocks??????Used?Available?Use%?Mounted?on /dev/sda1?????????????10325000???3529604???6270916??37%?/ /dev/sda3?????????????20646064???9595940??10001360??49%?/usr/local /dev/mapper/vg-data??103212320??26244284??71725156??27%?/data tmpfs?????????????????66128476??14709004??51419472??23%?/dev/shm none??????????????????20971520?????????0??20971520???0%?/tmp/tmpfs
?
于是我們就創(chuàng)建了一個(gè)新的tmpfs,空間是20G,我們可以在/tmp/tmpfs中創(chuàng)建一個(gè)20G以內(nèi)的文件。如果我們創(chuàng)建的文件實(shí)際占用的空間是內(nèi)存的話,那么這些數(shù)據(jù)應(yīng)該占用內(nèi)存空間的什么部分呢?根據(jù)pagecache的實(shí)現(xiàn)功能可以理解,既然是某種文件系統(tǒng),那么自然該使用pagecache的空間來(lái)管理。我們?cè)囋囀遣皇沁@樣?
?
[root@tencent64?~]#?free?-g ?????????????total???????used???????free?????shared????buffers?????cached Mem:???????????126?????????36?????????89??????????0??????????1?????????19 -/+?buffers/cache:?????????15????????111 Swap:????????????2??????????0??????????2 [root@tencent64?~]#?dd?if=/dev/zero?of=/tmp/tmpfs/testfile?bs=1G?count=13 13+0?records?in 13+0?records?out 13958643712?bytes?(14?GB)?copied,?9.49858?s,?1.5?GB/s [root@tencent64?~]#? [root@tencent64?~]#?free?-g ?????????????total???????used???????free?????shared????buffers?????cached Mem:???????????126?????????49?????????76??????????0??????????1?????????32 -/+?buffers/cache:?????????15????????110 Swap:????????????2??????????0??????????2
?
我們?cè)趖mpfs目錄下創(chuàng)建了一個(gè)13G的文件,并通過(guò)前后free命令的對(duì)比發(fā)現(xiàn),cached增長(zhǎng)了13G,說(shuō)明這個(gè)文件確實(shí)放在了內(nèi)存里并且內(nèi)核使用的是cache作為存儲(chǔ)。再看看我們關(guān)心的指標(biāo):-/+ buffers/cache那一行。我們發(fā)現(xiàn),在這種情況下free命令仍然提示我們有110G內(nèi)存可用,但是真的有這么多么?我們可以人工觸發(fā)內(nèi)存回收看看現(xiàn)在到底能回收多少內(nèi)存:
?
[root@tencent64?~]#?echo?3?>?/proc/sys/vm/drop_caches [root@tencent64?~]#?free?-g ?????????????total???????used???????free?????shared????buffers?????cached Mem:???????????126?????????43?????????82??????????0??????????0?????????29 -/+?buffers/cache:?????????14????????111 Swap:????????????2??????????0??????????2
?
可以看到,cached占用的空間并沒(méi)有像我們想象的那樣完全被釋放,其中13G的空間仍然被/tmp/tmpfs中的文件占用的。當(dāng)然,我的系統(tǒng)中還有其他不可釋放的cache占用著其余16G內(nèi)存空間。那么tmpfs占用的cache空間什么時(shí)候會(huì)被釋放呢?是在其文件被刪除的時(shí)候.如果不刪除文件,無(wú)論內(nèi)存耗盡到什么程度,內(nèi)核都不會(huì)自動(dòng)幫你把tmpfs中的文件刪除來(lái)釋放cache空間。
?
[root@tencent64?~]#?rm?/tmp/tmpfs/testfile? [root@tencent64?~]#?free?-g ?????????????total???????used???????free?????shared????buffers?????cached Mem:???????????126?????????30?????????95??????????0??????????0?????????16 -/+?buffers/cache:?????????14????????111 Swap:????????????2??????????0??????????2
?
這是我們分析的第一種cache不能被回收的情況。還有其他情況,比如:
共享內(nèi)存
共享內(nèi)存是系統(tǒng)提供給我們的一種常用的進(jìn)程間通信(IPC)方式,但是這種通信方式不能在shell中申請(qǐng)和使用,所以我們需要一個(gè)簡(jiǎn)單的測(cè)試程序,代碼如下:
?
[root@tencent64?~]#?cat?shm.c? #include?#include? #include? #include? #include? #include? #define?MEMSIZE?2048*1024*1023 int main() { ????int?shmid; ????char?*ptr; ????pid_t?pid; ????struct?shmid_ds?buf; ????int?ret; ????shmid?=?shmget(IPC_PRIVATE,?MEMSIZE,?0600); ????if?(shmid<0)?{ ????????perror("shmget()"); ????????exit(1); ????} ????ret?=?shmctl(shmid,?IPC_STAT,?&buf); ????if?(ret?0)?{ ????????perror("shmctl()"); ????????exit(1); ????} ????printf("shmid:?%d ",?shmid); ????printf("shmsize:?%d ",?buf.shm_segsz); ????buf.shm_segsz?*=?2; ????ret?=?shmctl(shmid,?IPC_SET,?&buf); ????if?(ret?0)?{ ????????perror("shmctl()"); ????????exit(1); ????} ????ret?=?shmctl(shmid,?IPC_SET,?&buf); ????if?(ret?0)?{ ????????perror("shmctl()"); ????????exit(1); ????} ????printf("shmid:?%d ",?shmid); ????printf("shmsize:?%d ",?buf.shm_segsz); ????pid?=?fork(); ????if?(pid<0)?{ ????????perror("fork()"); ????????exit(1); ????} ????if?(pid==0)?{ ????????ptr?=?shmat(shmid,?NULL,?0); ????????if?(ptr==(void*)-1)?{ ????????????perror("shmat()"); ????????????exit(1); ????????} ????????bzero(ptr,?MEMSIZE); ????????strcpy(ptr,?"Hello!"); ????????exit(0); ????}?else?{ ????????wait(NULL); ????????ptr?=?shmat(shmid,?NULL,?0); ????????if?(ptr==(void*)-1)?{ ????????????perror("shmat()"); ????????????exit(1); ????????} ????????puts(ptr); ????????exit(0); ????} }
?
程序功能很簡(jiǎn)單,就是申請(qǐng)一段不到2G共享內(nèi)存,然后打開(kāi)一個(gè)子進(jìn)程對(duì)這段共享內(nèi)存做一個(gè)初始化操作,父進(jìn)程等子進(jìn)程初始化完之后輸出一下共享內(nèi)存的內(nèi)容,然后退出。但是退出之前并沒(méi)有刪除這段共享內(nèi)存。我們來(lái)看看這個(gè)程序執(zhí)行前后的內(nèi)存使用:
?
[root@tencent64?~]#?free?-g ?????????????total???????used???????free?????shared????buffers?????cached Mem:???????????126?????????30?????????95??????????0??????????0?????????16 -/+?buffers/cache:?????????14????????111 Swap:????????????2??????????0??????????2 [root@tencent64?~]#?./shm? shmid:?294918 shmsize:?2145386496 shmid:?294918 shmsize:?-4194304 Hello! [root@tencent64?~]#?free?-g ?????????????total???????used???????free?????shared????buffers?????cached Mem:???????????126?????????32?????????93??????????0??????????0?????????18 -/+?buffers/cache:?????????14????????111 Swap:????????????2??????????0??????????2 cached空間由16G漲到了18G。那么這段cache能被回收么?繼續(xù)測(cè)試: [root@tencent64?~]#?echo?3?>?/proc/sys/vm/drop_caches [root@tencent64?~]#?free?-g ?????????????total???????used???????free?????shared????buffers?????cached Mem:???????????126?????????32?????????93??????????0??????????0?????????18 -/+?buffers/cache:?????????14????????111 Swap:????????????2??????????0??????????2
?
結(jié)果是仍然不可回收。大家可以觀察到,這段共享內(nèi)存即使沒(méi)人使用,仍然會(huì)長(zhǎng)期存放在cache中,直到其被刪除。刪除方法有兩種,一種是程序中使用shmctl()去IPC_RMID,另一種是使用ipcrm命令。我們來(lái)刪除試試:
?
[root@tencent64?~]#?ipcs?-m ------?Shared?Memory?Segments?-------- key????????shmid??????owner??????perms??????bytes??????nattch?????status?????? 0x00005feb?0??????????root???????666????????12000??????4??????????????????????? 0x00005fe7?32769??????root???????666????????524288?????2??????????????????????? 0x00005fe8?65538??????root???????666????????2097152????2??????????????????????? 0x00038c0e?131075?????root???????777????????2072???????1??????????????????????? 0x00038c14?163844?????root???????777????????5603392????0??????????????????????? 0x00038c09?196613?????root???????777????????221248?????0??????????????????????? 0x00000000?294918?????root???????600????????2145386496?0??????????????????????? [root@tencent64?~]#?ipcrm?-m?294918 [root@tencent64?~]#?ipcs?-m ------?Shared?Memory?Segments?-------- key????????shmid??????owner??????perms??????bytes??????nattch?????status?????? 0x00005feb?0??????????root???????666????????12000??????4??????????????????????? 0x00005fe7?32769??????root???????666????????524288?????2??????????????????????? 0x00005fe8?65538??????root???????666????????2097152????2??????????????????????? 0x00038c0e?131075?????root???????777????????2072???????1??????????????????????? 0x00038c14?163844?????root???????777????????5603392????0??????????????????????? 0x00038c09?196613?????root???????777????????221248?????0??????????????????????? [root@tencent64?~]#?free?-g ?????????????total???????used???????free?????shared????buffers?????cached Mem:???????????126?????????30?????????95??????????0??????????0?????????16 -/+?buffers/cache:?????????14????????111 Swap:????????????2??????????0??????????2
?
刪除共享內(nèi)存后,cache被正常釋放了。這個(gè)行為與tmpfs的邏輯類似。內(nèi)核底層在實(shí)現(xiàn)共享內(nèi)存(shm)、消息隊(duì)列(msg)和信號(hào)量數(shù)組(sem)這些POSIX:XSI的IPC機(jī)制的內(nèi)存存儲(chǔ)時(shí),使用的都是tmpfs。這也是為什么共享內(nèi)存的操作邏輯與tmpfs類似的原因。當(dāng)然,一般情況下是shm占用的內(nèi)存更多,所以我們?cè)诖酥攸c(diǎn)強(qiáng)調(diào)共享內(nèi)存的使用。說(shuō)到共享內(nèi)存,Linux還給我們提供了另外一種共享內(nèi)存的方法,就是:
mmap
mmap()是一個(gè)非常重要的系統(tǒng)調(diào)用,這僅從mmap本身的功能描述上是看不出來(lái)的。從字面上看,mmap就是將一個(gè)文件映射進(jìn)進(jìn)程的虛擬內(nèi)存地址,之后就可以通過(guò)操作內(nèi)存的方式對(duì)文件的內(nèi)容進(jìn)行操作。但是實(shí)際上這個(gè)調(diào)用的用途是很廣泛的。當(dāng)malloc申請(qǐng)內(nèi)存時(shí),小段內(nèi)存內(nèi)核使用sbrk處理,而大段內(nèi)存就會(huì)使用mmap。當(dāng)系統(tǒng)調(diào)用exec族函數(shù)執(zhí)行時(shí),因?yàn)槠浔举|(zhì)上是將一個(gè)可執(zhí)行文件加載到內(nèi)存執(zhí)行,所以內(nèi)核很自然的就可以使用mmap方式進(jìn)行處理。我們?cè)诖藘H僅考慮一種情況,就是使用mmap進(jìn)行共享內(nèi)存的申請(qǐng)時(shí),會(huì)不會(huì)跟shmget()一樣也使用cache?
同樣,我們也需要一個(gè)簡(jiǎn)單的測(cè)試程序:
?
[root@tencent64?~]#?cat?mmap.c? #include?#include? #include? #include? #include? #include? #include? #include? #define?MEMSIZE?1024*1024*1023*2 #define?MPFILE?"./mmapfile" int?main() { ?void?*ptr; ?int?fd; ?fd?=?open(MPFILE,?O_RDWR); ?if?(fd?0)?{ ??perror("open()"); ??exit(1); ?} ?ptr?=?mmap(NULL,?MEMSIZE,?PROT_READ|PROT_WRITE,?MAP_SHARED|MAP_ANON,?fd,?0); ?if?(ptr?==?NULL)?{ ??perror("malloc()"); ??exit(1); ?} ?printf("%p ",?ptr); ?bzero(ptr,?MEMSIZE); ?sleep(100); ?munmap(ptr,?MEMSIZE); ?close(fd); ?exit(1); }
?
這次我們干脆不用什么父子進(jìn)程的方式了,就一個(gè)進(jìn)程,申請(qǐng)一段2G的mmap共享內(nèi)存,然后初始化這段空間之后等待100秒,再解除影射所以我們需要在它sleep這100秒內(nèi)檢查我們的系統(tǒng)內(nèi)存使用,看看它用的是什么空間?當(dāng)然在這之前要先創(chuàng)建一個(gè)2G的文件./mmapfile。結(jié)果如下:
?
[root@tencent64?~]#?dd?if=/dev/zero?of=mmapfile?bs=1G?count=2 [root@tencent64?~]#?echo?3?>?/proc/sys/vm/drop_caches [root@tencent64?~]#?free?-g ?????????????total???????used???????free?????shared????buffers?????cached Mem:???????????126?????????30?????????95??????????0??????????0?????????16 -/+?buffers/cache:?????????14????????111 Swap:????????????2??????????0??????????2
?
然后執(zhí)行測(cè)試程序:
?
[root@tencent64?~]#?./mmap?& [1]?19157 0x7f1ae3635000 [root@tencent64?~]#?free?-g ?????????????total???????used???????free?????shared????buffers?????cached Mem:???????????126?????????32?????????93??????????0??????????0?????????18 -/+?buffers/cache:?????????14????????111 Swap:????????????2??????????0??????????2 [root@tencent64?~]#?echo?3?>?/proc/sys/vm/drop_caches [root@tencent64?~]#?free?-g ?????????????total???????used???????free?????shared????buffers?????cached Mem:???????????126?????????32?????????93??????????0??????????0?????????18 -/+?buffers/cache:?????????14????????111 Swap:????????????2??????????0??????????2
?
我們可以看到,在程序執(zhí)行期間,cached一直為18G,比之前漲了2G,并且此時(shí)這段cache仍然無(wú)法被回收。然后我們等待100秒之后程序結(jié)束。
?
[root@tencent64?~]#? [1]+??Exit?1??????????????????./mmap [root@tencent64?~]#? [root@tencent64?~]#?free?-g ?????????????total???????used???????free?????shared????buffers?????cached Mem:???????????126?????????30?????????95??????????0??????????0?????????16 -/+?buffers/cache:?????????14????????111 Swap:????????????2??????????0??????????2
?
程序退出之后,cached占用的空間被釋放。這樣我們可以看到,使用mmap申請(qǐng)標(biāo)志狀態(tài)為MAP_SHARED的內(nèi)存,內(nèi)核也是使用的cache進(jìn)行存儲(chǔ)的。在進(jìn)程對(duì)相關(guān)內(nèi)存沒(méi)有釋放之前,這段cache也是不能被正常釋放的。實(shí)際上,mmap的MAP_SHARED方式申請(qǐng)的內(nèi)存,在內(nèi)核中也是由tmpfs實(shí)現(xiàn)的。由此我們也可以推測(cè),由于共享庫(kù)的只讀部分在內(nèi)存中都是以mmap的MAP_SHARED方式進(jìn)行管理,實(shí)際上它們也都是要占用cache且無(wú)法被釋放的。
最后
我們通過(guò)三個(gè)測(cè)試?yán)樱l(fā)現(xiàn)Linux系統(tǒng)內(nèi)存中的cache并不是在所有情況下都能被釋放當(dāng)做空閑空間用的。并且也也明確了,即使可以釋放cache,也并不是對(duì)系統(tǒng)來(lái)說(shuō)沒(méi)有成本的??偨Y(jié)一下要點(diǎn),我們應(yīng)該記得這樣幾點(diǎn):
當(dāng)cache作為文件緩存被釋放的時(shí)候會(huì)引發(fā)IO變高,這是cache加快文件訪問(wèn)速度所要付出的成本。
tmpfs中存儲(chǔ)的文件會(huì)占用cache空間,除非文件刪除否則這個(gè)cache不會(huì)被自動(dòng)釋放。
使用shmget方式申請(qǐng)的共享內(nèi)存會(huì)占用cache空間,除非共享內(nèi)存被ipcrm或者使用shmctl去IPC_RMID,否則相關(guān)的cache空間都不會(huì)被自動(dòng)釋放。
使用mmap方法申請(qǐng)的MAP_SHARED標(biāo)志的內(nèi)存會(huì)占用cache空間,除非進(jìn)程將這段內(nèi)存munmap,否則相關(guān)的cache空間都不會(huì)被自動(dòng)釋放。
實(shí)際上shmget、mmap的共享內(nèi)存,在內(nèi)核層都是通過(guò)tmpfs實(shí)現(xiàn)的,tmpfs實(shí)現(xiàn)的存儲(chǔ)用的都是cache。
當(dāng)理解了這些的時(shí)候,希望大家對(duì)free命令的理解可以達(dá)到我們說(shuō)的第三個(gè)層次。我們應(yīng)該明白,內(nèi)存的使用并不是簡(jiǎn)單的概念,cache也并不是真的可以當(dāng)成空閑空間用的。如果我們要真正深刻理解你的系統(tǒng)上的內(nèi)存到底使用的是否合理,是需要理解清楚很多更細(xì)節(jié)知識(shí),并且對(duì)相關(guān)業(yè)務(wù)的實(shí)現(xiàn)做更細(xì)節(jié)判斷的。我們當(dāng)前實(shí)驗(yàn)場(chǎng)景是Centos 6的環(huán)境,不同版本的Linux的free現(xiàn)實(shí)的狀態(tài)可能不一樣,大家可以自己去找出不同的原因。
當(dāng)然,本文所述的也不是所有的cache不能被釋放的情形。那么,在你的應(yīng)用場(chǎng)景下,還有那些cache不能被釋放的場(chǎng)景呢?
審核編輯:湯梓紅
評(píng)論
查看更多