瞭解Linux 內存使用

分享一下我老師大神的人工智能教程。零基礎!通俗易懂!風趣幽默!還帶黃段子!希望你也加入到我們人工智能的隊伍中來!https://blog.csdn.net/jiangjunshow

               

1. 用戶進程內存結構

top 命令瞭解進程信息,其中包括內存方面的信息。

正在運行的程序,叫進程。每個進程都有完全屬於自己的,獨立的,不被幹擾的內存空間。此空間,被分成幾個段(Segment),分別是Text, Data, BSS, Heap, Stack。用戶進程內存空間,也是系統內核分配給該進程的VM(虛擬內存),但並不表示這個進程佔用了這麼多的RAM(物理內存)。這個空間有多大?命令top輸出的VIRT值告訴了我們各個進程內存空間的大小(進程內存空間隨着程序的執行會增大或者縮小)。你還可以通過/proc/$pid/maps,或者pmap –d 瞭解某個進程內存空間都分佈,比如:

每臺主機都維護一個ARP緩存表,可以用arp -a命令查看。

[root@localhost4 vhosts]#  pmap  19254      -x
19254:   nginx: master process /usr/local/app/nginx/sbin/nginx
Address           Kbytes     RSS   Dirty Mode   Mapping
0000000000400000     552     224       0 r-x--  nginx
0000000000689000      72      60      60 rw---  nginx
000000000069b000      56      12      12 rw---    [ anon ]
0000000000950000     652     636     636 rw---    [ anon ]
00000037c3a00000      92      44       0 r-x--  libpthread-2.12.so
00000037c3a17000    2048       0       0 -----  libpthread-2.12.so
00000037c3c17000       4       4       4 r----  libpthread-2.12.so
00000037c3c18000       4       4       4 rw---  libpthread-2.12.so

擴展和設備格式區域
Address: 內存開始地址
Kbytes: 佔用內存的字節數(KB)
RSS: 保留內存的字節數(KB)
Dirty: 髒頁的字節數(包括共享和私有的)(KB)
Mode: 內存的權限:read、write、execute、shared、private (寫時複製)
Mapping: 佔用內存的文件、或[anon](分配的內存)、或[stack](堆棧)
Offset: 文件偏移
Device: 設備名 (major:minor)


當fork()或者exec()一個進程的時候,系統內核就會分配一定量的VM給進程,作爲進程的內存空間,大小由BSS段,Data段的已定義的全局變量、靜態變量、Text段中的字符直接量、程序本身的內存映像等,還有Stack段的局部變量決定。當然,還可以通過malloc()等函數動態分配內存,向上擴大heap。

動態分配與靜態分配,二者最大的區別在於:

1. 直到Run-Time的時候,執行動態分配,而在compile-time的時候,就已經決定好了分配多少Text+Data+BSS+Stack。

2.通過malloc()動態分配的內存,需要程序員手工調用free()釋放內存,否則容易導致內存泄露,而靜態分配的內存則在進程執行結束後系統釋放(Text, Data), 但Stack段中的數據很短暫,函數退出立即被銷燬。

示例小程序,加深理解:

/* @filename: example-2.c */

#include <stdio.h>int main(int argc, char *argv[]{    char arr[] = "hello world";     /* Stack段,rw--- */    char *p = "hello world";        /* Text段,字符串直接量, r-x--  */    arr[1] = 'l';    *(++p) = 'l';   /* 出錯了,Text段不能write */    return 0;}
變量p,它在Stack段,但它所指的”hello world”是一個字符串直接量,放在Text段。
/* @filename:example_2_2.c */
#include <stdio.h>#include <stdlib.h>#include <string.h>char *get_str_1(){    char str[] = "hello world";    return str;}char *get_str_2() {    char *str = "hello world";    return str;}char *get_str_3(){    char tmp[] = "hello world";    char *str;    str = (char *)malloc(12 * sizeof(char));    memcpy(str, tmp, 12);    return str;}int main(int argc, char *argv[])  {    char *str_1 = get_str_1();  //出錯了,Stack段中的數據在函數退出時就銷燬了    char *str_2 = get_str_2();  //正確,指向Text段中的字符直接量,退出程序後纔會回收    char *str_3 = get_str_3();  //正確,指向Heap段中的數據,還沒free()    printf("%s\n", str_1);    printf("%s\n", str_2);    printf("%s\n", str_3);    if (str_3 != NULL)   {        free(str_3);        str_3 = NULL;    }    return 0;}
函數get_str_1()返回Stack段數據,編譯時會報錯:

example_2_3.c: In function ‘get_str_1’:
example_2_3.c:7: warning: function returns address of local variable

Heap中的數據,如果不用了,應該儘早釋放free()。

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h> char data_var  = '1';char *mem_killer(){   char *p;   p = (char *)malloc(1024*1024*4);   memset(p, '\0', 1024*1024*4);   p = &data_var;   //危險,內存泄露   return p;} int main(int argc, char *argv[]){    char *p;    for (;;)    {        p = mem_killer(); // 函數中malloc()分配的內存沒辦法free()        printf("%c\n", *p);        sleep(20);    }    return 0;}

使用malloc(),特別要留意heap段中的內存不用時,儘早手工free()。通過top輸出的VIRT和RES兩值來觀察進程佔用VM和RAM大小。
因爲Text, BSS, Data段在編譯時已經決定了進程將佔用多少VM。可以通過size,知道這些信息:

[root@localhost4 ~]# gcc example_2_3.c  -o example_2_3
[root@localhost4 ~]# size example_2_3
   text    data     bss     dec     hex filename
   1503     520      16    2039     7f7 example_2_3

2. 用戶進程內存分配malloc

編碼人員在編寫程序之際,時常要處理變化數據,無法預料要處理的數據集變化是否大,所以除了變量之外,還需要動態分配內存。GNU libc庫提供了二個內存分配函數,分別是malloc()和calloc()。調用malloc(size_t size)函數分配內存成功,總會分配size字節VM(再次強調不是RAM),並返回一個指向剛纔所分配內存區域的開端地址。分配的內存會爲進程一直保留着,直到你顯示地調用free()釋放它(當然,整個進程結束,靜態和動態分配的內存都會被系統回收)。開發人員有責任儘早將動態分配的內存釋放回系統。記住一句話:儘早free()!

我們來看看,malloc()小示例:

/* @filename:example_2_4.c */

#include <stdio.h>#include <stdlib.h> int main(int argc, char *argv[]){    char *p_4kb, *p_128kb, *p_300kb;    if ((p_4kb = malloc(4*1024)) != NULL){        free(p_4kb);
</pre><pre name="code" class="cpp" style="font-size: 16px; line-height: 25.6px; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif; color: rgb(62, 62, 62);">    }    if ((p_128kb = malloc(128*1024)) != NULL) {        free(p_128kb);    }    if ((p_300kb = malloc(300*1024)) != NULL)  {        free(p_300kb);    }    return 0;}

#gcc example_2_4.c –o example_2_4
#strace –t ./example_2_4

00:02:53 brk(0)                         = 0x8f58000
00:02:53 brk(0x8f7a000)                 = 0x8f7a000
00:02:53 brk(0x8f79000)                 = 0x8f79000
00:02:53 mmap2(NULL, 311296, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb772d000
00:02:53 munmap(0xb772d000, 311296)     = 0
系統調用brk(0)取得當前堆的地址,也稱爲斷點。

通過跟蹤系統內核調用,可見glibc函數malloc()總是通過brk()或mmap()系統調用來滿足內存分配需求。函數malloc(),根據不同大小內存要求來選擇brk(),還是mmap() 128Kbytes是臨界值:

1) 小塊內存(<=128kbytes),會調用brk(),它將數據段的最高地址往更高處推(堆從底部向上增長)。

2)大塊內存,則使用mmap()進行匿名映射(設置標誌MAP_ANONYMOUS)來分配內存,與堆無關,在堆之外。

這樣做是有道理的,試想:如果大塊內存,也調用brk(),則容易被小塊內存釘住,必竟用大塊內存不是很頻繁;反過來,小塊內存分配更爲頻繁得多,如果也使用mmap(),頻繁的創建內存映射會導致更多的開銷,還有一點就是,內存映射的大小要求必須是“頁”(單位,內存頁面大小,默認4Kbytes或8Kbytes)的倍數,如果只是爲了”hello world”這樣小數據就映射一“頁”內存,那實在是太浪費了。

跟malloc()一樣,釋放內存函數free(),也會根據內存大小,選擇使用brk()將斷點往低處回推,或者選擇調用munmap()解除映射。有一點需要注意:並不是每次調用free()小塊內存,都會馬上調用brk(),即堆並不會在每次內存被釋放後就被縮減,而是會被glibc保留給下次malloc()使用(必竟小塊內存分配較爲頻繁),直到glibc發現堆空閒大小顯著大於內存分配所需數量時,則會調用brk()。但每次free()大塊內存,都會調用munmap()解除映射.

3. 缺頁處理

每次調用malloc(),系統都只是給進程分配線性地址(VM),並沒有隨即分配頁框(RAM)。系統儘量將分配頁框的工作推遲到最後一刻—用到時缺頁異常處理。這種頁框按需延遲分配策略最大好處之一:充分有效地善用系統稀缺資源RAM。

當指針引用的內存頁沒有駐留在RAM中,即在RAM找不到與之對應的頁框,則會發生缺頁異常(對進程來說是透明的),內核便陷入缺頁異常處理。發生缺頁異常有幾種情況:

1.只分配了線性地址,並沒有分配頁框,常發生在第一次訪問某內存頁。

2.已經分配了頁框,但頁框被回收,換出至磁盤(交換區)。

3.引用的內存頁,在進程空間之外,不屬於該進程,可能已被free()。

我們使用一段僞代碼來大致瞭解缺頁異常。

/* @filename: example_2_5.c */…demo() {    char *p;    if ((p = malloc(1024*100)) != NULL// L0:分配了100Kbytes線性地址    {        *p = 't';      // L1         …         *p = 'm';      // L2:過去了很長一段時間,不管系統忙否,長久不用的頁框都有可能被回收         p[4096] = 'p';// L3          …        free(p);       //L4  if (p == NULL) {   *p = 'l'// L5  }    }}…
  • L0,函數malloc()通過brk()給進程分配了100Kbytes的線性地址區域(VM).然而,系統並沒有隨即分配頁框(RAM)。即此時,進程沒有佔用100Kbytes的物理內存。這也表明了,你時常在使用top的時候VIRT值增大,而RES值卻不變的原因。

  • L1,通過*p引用了100Kbytes的第一頁(4Kbytes)。因爲是第一次引用此頁,在RAM中找不到與之相對應的頁框。發生缺頁異常(對於進程而言缺頁異常是透明的),系統靈敏地捕獲這一異常,進入缺頁異常處理階段:接下來,系統會分配一個頁框(RAM)映射給它。我們把這種情況(被訪問的頁還沒有被放在任何一個頁框中,內核分配一新的頁框並適當初始化來滿足調用請求),也稱爲Demand Paging。

  • L2,過了很長一段時間,通過*p再次引用100Kbytes的第一頁。若系統在RAM找不到它映射的頁框(可能交換至磁盤了)。發生缺頁異常,並被系統捕獲進入缺頁異常處理。接下來,系統則會分配一頁頁框(RAM),找到備份在磁盤的那“頁”,並將它換入內存(其實因爲換入操作比較昂貴,所以不總是隻換入一頁,而是預換入多頁。這也表明某些文檔說:”vmstat某時出現不少si並不能意味着物理內存不足”)。凡是類似這種會迫使進程去睡眠(很可能是由於當前磁盤數據填充至頁框(RAM)所花的時間),阻塞當前進程的缺頁異常處理稱爲主缺頁(major falut),也稱爲大缺頁(參見下圖)。相反,不會阻塞進程的缺頁,稱爲次缺頁(minor fault),也稱爲小缺面。

  • L3,引用了100Kbytes的第二頁。參見第一次訪問100Kbytes第一頁, Demand Paging。

  • L4,釋放了內存:線性地址區域被刪除,頁框也被釋放。

  • L5,再次通過*p引用內存頁,已被free()了(用戶進程本身並不知道)。發生缺頁異常,缺面異常處理程序會檢查出這個缺頁不在進程內存空間之內。對待這種編程錯誤引起的缺頁異常,系統會殺掉這個進程,並且報告著名的段錯誤(Segmentation fault)。

4. 頁框回收SWAP

隨着網絡併發用戶數量增多,進程數量越來越多(比如一般守護進程會fork()子進程來處理用戶請求),缺頁異常也就更頻繁,需要緩存更多的磁盤數據(參考下篇OS Page Cache),RAM也就越來越緊少。爲了保證有夠用的頁框供給缺頁異常處理,Linux有一套自己的做法,稱爲PFRA。PFRA總會從用戶態進內存程空間和頁面緩存中,“竊取”頁框滿足供給。所謂”竊取”,指的是:將用戶進程內存空間對應占用的頁框中的數據swap out至磁盤(稱爲交換區),或者將OS頁面緩存中的內存頁(還有用戶進程mmap()的內存頁)flush(同步fsync())至磁盤設備。

swap out: 由於RAM資源不足,PFRA會將部分匿名頁框的數據寫入到交換區(swap area)(磁盤),備份之,這個動作稱爲so(swap out)。

swap in:等到發生內存缺頁異常的時候,缺頁異常處理程序會將交換區(磁盤)的頁面又讀回物理內存,這個動作稱爲si(swap in)。

每次Swapping,都有可能不只是一頁數據,不管是si,還是so。Swapping意味着磁盤操作,更新頁表等操作,這些操作開銷都不小,會阻塞用戶態進程。所以,持續飈高的si/so意味着物理內存資源是性能瓶頸。

如果你觀察到因爲RAM不足導致系統病態式般慢,通常都是因爲缺頁異常處理,以及PFRA在”盜頁”。我們從以下幾個方面瞭解PFRA。

候選頁框:找出哪些頁框是可以被回收?

  • 進程內存空間佔用的頁框,比如數據段中的頁(Heap, Data),還有在Heap與Stack之間的匿名映射頁(比如由malloc()分配的大內存)。但不包括Stack段中的頁。

  • 進程空間mmap()的內存頁,有映射文件,非匿名映射。

  • 緩存在頁面緩存中Buffer/Cache佔用的頁框。也稱OS Page Cache。

頁框回收策略:確定了要回收的頁框,就要進一步確定先回收哪些候選頁框

  • 儘量先回收頁面緩存中的Buffer/Cache。其次再回收內存空間佔用的頁框。

  • 進程空間佔用的頁框,要是沒有被鎖定,都可以回收。所以,當某進程睡眠久了,佔用的頁框會逐漸地交換出去至交換區。

  • 使收LRU置換算法,將那些久而未用的頁框優先被回收。這種被放在LRU的unused鏈表的頁,常被認爲接下來也不太可能會被引用。

  • 相對回收Buffer/Cache而言,回收進程內存頁,昂貴很多。所以,Linux默認只有swap_tendency(交換傾向值)值不小於100時,纔會選擇換出進程佔用的RES。其實交換傾向值描述的是:系統越忙,且RES都被進程佔用了,Buffer/Cache只佔了一點點的時候,纔開始回收進程佔用頁框。PS:這正表明了,某些DBA提議將MySQL InnoDB服務器vm.swappiness值設置爲0,以此讓InnoDB Buffer Pool數據在RES呆得更久。

  • 如果實在是沒有頁框可回收,PFRA使出最狠一招,殺掉一個用戶態進程,並釋放這些被佔的頁框。當然,這個被殺的進程不是胡亂選的,至少應該是佔用較多頁框,運行優選級低,且不是root用戶的進程。

激活回收頁框:什麼時候會回收頁框?

  • 緊急回收。系統內核發現沒有夠用的頁框分配,供給讀文件和內存缺頁處理的時候,系統內核開始”緊急回收頁框”。喚醒pdflush內核線程,先將1024頁髒頁從頁面緩存寫回磁盤。然後開始回收32頁框,若反覆回收13次,還收不齊32頁框,則發狠殺一個進程。

  • 週期性回收。在緊急回收之前,PFRA還會喚醒內核線程kswapd。爲了避免更多的“緊急回收”,當發現空閒頁框數量低於設置的警告值時,內核線程kswapd就會被喚醒,回收頁框。直到空閒的頁框的數量達到設定的安全值。PS:當RES資源緊張的時候,你可以通過ps命令看到更多的kswapd線程被喚醒。

  • OOM。在高峯時期,RES高度緊張的時候,kswapd持續回收的頁框供不應求,直到進入”緊急回收”,直到 OOM。

5. linux內存信息

我們通過free的輸出內存內容:
#free
total used free shared buffers cached
Mem: 16402432 16360492 41940 0 465404 12714880
-/+ buffers/cache: 3180208 13222224
Swap: 8193108 264 8192844

total:物理內存的總大小。used:已經使用的物理內存多小。
free:空閒的物理內存值。
shared:多個進程共享的內存值。
buffers/cached:磁盤緩存的大小。
第三行(-/+ buffers/cached):代表磁盤緩存使用狀態。
第四行:Swap表示交換空間內存使用狀態。
公式:
-buffers/cache 的內存數:used - buffers - cached
+buffers/cache 的內存數:free + buffers + cached
可用的memory=free memory+buffers+cached
free命令輸出的內存狀態,可以通過兩個角度來查看:一個是從內核的角度來看,一個是從應用層的角度來看的。


1.從內核的角度來查看內存的狀態:(對於OS,buffers/cached 都是屬於被使用)
        內核目前可以直接分配到,不需要額外的操作,即爲上面free命令輸出中第二行Mem項的值,可以看出,此係統物理內存有4G,空閒的內存只有41940K,也就是40M多一點,我們來做一個這樣的計算:
       16402432-16360492=41940
       就是總的物理內存減去已經使用的物理內存得到的就是空閒的物理內存大小。
       注意:這裏的可用內存值41940並不包含處於buffers和cached狀態的內存大小。
      注意:實際上,內核完全控制着內存的使用情況,linux會在需要內存的時候,或在系統運行逐步推進時,將buffers和cached狀態的內存變爲free狀態的內存,以供系統使用。

2.從應用層的角度來看系統內存的使用狀態
      也就是linux上運行的應用程序可以使用的內存大小,即free命令第三行“(-/+ buffers/cached)”的輸出,可以看到,此係統已經使用的內存才3180208K,而空閒的內存達到13222224K,繼續做這樣一個計算:
      41940(Men:free)+(465404(Men:buffers)+12714880(Men:cached))=13222224(-/+buffers/cached:free)
      通過這個等式可知,應用程序可用的物理內存值是Mem項的free值加上buffers和cached值之和,也就是說,這個free值是包括buffers和cached項大小的。
       對於應用程序來說,buffers/cached佔有的內存是可用的,因爲buffers/cached是爲了提高文件讀取的性能,當應用程序需要用到內存的時候buffers/cached會很快地被回收,以供應用程序使用


6. buffer和cache是什麼

1)、計算機領域的buffer和cache

buffer和cache是兩個在計算機技術中被用濫的名詞,放在不通語境下會有不同的意義。

Cache:高速緩存,是位於CPU與主內存間的一種容量較小但速度很高的存儲器。由於CPU的速度遠高於主內存,CPU直接從內存中存取數據要等待一定時間週期,Cache中保存着CPU剛用過或循環使用的一部分數據,當CPU再次使用該部分數據時可從Cache中直接調用這樣就減少了CPU的等待時間提高了系統的效率。Cache又分爲一級Cache(L1 Cache)和二級Cache(L2 Cache),L1 Cache集成在CPU內部,L2 Cache早期一般是焊在主板上現在也都集成在CPU內部,常見的容量有256KB或512KB L2 Cache

Buffer:緩衝區,一個用於存儲速度不同步的設備或優先級不同的設備之間傳輸數據的區域。通過緩衝區,可以使進程之間的相互等待變少,從而使從速度慢的設備讀入數據時,速度快的設備的操作進程不發生間斷。

cache最初用於cpu cache, 主要原因是cpu 與memory, 由於cpu快,memory跟不上,且有些值使用次數多,所以放入cache中,主要目的是,重複使用, 並且一級\二級物理cache速度快,
buffer主要用於disk與 memory,主要是保護硬盤或減少網絡傳輸的次數(內存數據表現dataSet).當然也可以提高速度(不會立即寫入硬盤或直接從硬盤中讀出的數據馬上顯示),重複使用,最初最主要的目的是保護disk。

2)、linux Free的buffer和cache

linuxFree中的buffer和cache:(它們都是佔用內存):

Linux操作系統中,當應用程序需要讀取文件中的數據時,操作系統先分配一些內存,將數據從磁盤讀入到這些內存中,然後

再將數據分發給應用程序;當需要往文件中寫數據時,操作系統先分配內存接收用戶數據,然後再將數據從內存寫到磁盤上。然而,如果有大量數據需要從磁盤讀取到內存或者由內存寫入磁盤時,系統的讀寫性能就變得非常低下,因爲無論是從磁盤讀數據,還是寫數據到磁盤,都是一個很消耗時間和資源的過程,在這種情況下,爲了提高磁盤存取效率, Linux做了一些精心的設計, 除了對dentry進行緩存(用於VFS,加速文件路徑名到inode的轉換), 還採取了兩種主要Cache方式:Buffer Cache和Page Cache。前者針對磁盤塊的讀寫,後者針對文件inode的讀寫。這些Cache有效縮短了 I/O系統調用(比如read,write,getdents)的時間。"

buffers與cached都是內存操作,用來保存系統曾經打開過的文件以及文件屬性信息,這樣當操作系統需要讀取某些文件時,會首先在buffers與cached內存區查找,如果找到,直接讀出傳送給應用程序,如果沒有找到需要數據,才從磁盤讀取,這就是操作系統的緩存機制,通過緩存,大大提高了操作系統的性能。但buffers與cached緩衝的內容卻是不同的。

buffers:作爲buffer cache的內存,是用來緩衝塊設備做的,對塊的操作會使用buffer cache進行緩存,它只記錄文件系統的元數據(metadata)以及 tracking in-flight pages。比如我們在格式化文件系統的時候,查找系統文件等。

cached:作爲page cache的內存,是用來給文件做緩衝。如果 cache 的值很大,說明cache住的文件數很多。如果頻繁訪問到的文件都能被cache住,那麼磁盤的讀IO bi會非常小。

更通俗一點說:buffers主要用來存放目錄裏面有什麼內容\文件的屬性\權限等等。cached直接用來記憶我們打開過的文件和程序。

一般情況下兩個緩存系統是一起配合使用的:

磁盤的操作有邏輯級(文件系統)和物理級(磁盤塊),這兩種Cache就是分別緩存邏輯和物理級數據的。
Page cache實際上是針對文件系統的,是文件的緩存,在文件層面上的數據會緩存到page cache。文件的邏輯層需要映射到實際的物理磁盤,這種映射關係由文件系統來完成。當page cache的數據需要刷新時,page cache中的數據交給buffer cache,因爲Buffer Cache就是緩存磁盤塊的。但是這種處理在2.6版本的內核之後就變的很簡單了,沒有真正意義上的cache操作。
Buffer cache是針對磁盤塊的緩存,也就是在沒有文件系統的情況下,直接對磁盤進行操作的數據會緩存到buffer cache中,例如,文件系統的元數據都會緩存到buffer cache中。
簡單說來,page cache用來緩存文件數據,buffer cache用來緩存磁盤數據。在有文件系統的情況下,對文件操作,那麼數據會緩存到page cache,如果直接採用dd等工具對磁盤進行讀寫,那麼數據會緩存到buffer cache。

比如當我們對一個文件進行寫操作的時候,page cache的內容會被改變,而buffer cache則可以用來將page標記爲不同的緩衝區,並記錄是哪一個緩衝區被修改了。

這樣,內核在後續執行髒數據的回寫(writeback)時,就不用將整個page寫回,而只需要寫回修改的部分即可。

爲了驗證我們的結論是否正確。

buffers驗證

我們在執行find /* -name *.conf之前的free數值:

[root@localhost4 ~]# free -m
             total       used       free     shared    buffers     cached
Mem:   3740        649       3090          0         74        123
-/+ buffers/cache:        450       3289
Swap:         3871          0       3871

find /* -name *.conf

看看buffers的值是否變化:

             total       used       free     shared    buffers     cached
Mem:          3740       1183       2556          0        486        123
-/+ buffers/cache:        573       3166
Swap:         3871          0       3871

buffers爲486MB, cached不變 ,buffers變大啦

然後重複執行find命令,看看兩次顯示速度有何不同。

cached驗證:

cached:可以通過vi打開一個非常大的文件,看看cached的變化,然後再次vi這個文件,感覺一下兩次打開的速度有何異同,是不是第二次打開的速度明顯快於第一次呢?此外我們通過複製文件: 


[root@localhost4 ~]#  cp -r /etc ~/test/
[root@localhost4 ~]# free -m   
             total       used       free     shared    buffers     cached
Mem:      3740       1261       2479          0        487        194
-/+ buffers/cache:        579       3160
Swap:         3871          0       3871

都cached已經變大啦。

Linux操作系統的內存運行原理,很大程度上是根據服務器的需求來設計的,例如系統的緩衝機制會把經常使用到的文件和數據緩存在cached中,linux總是在力求緩存更多的數據和信息,這樣再次需要這些數據時可以直接從內存中取,而不需要有一個漫長的磁盤操作,這種設計思路提高了系統的整體性能。

2、如何回收cached

Linux內核會在內存將要耗盡的時候,觸發內存回收的工作,以便釋放出內存給急需內存的進程使用。
一般情況下,這個操作中主要的內存釋放都來自於對buffer/cache的釋放。尤其是被使用更多的cache空間。既然它主要用來做緩存,只是在內存夠用的時候加快進程對文件的讀寫速度,那麼在內存壓力較大的情況下,當然有必要清空釋放cache,作爲free空間分給相關進程使用。
所以一般情況下,我們認爲buffer/cache空間可以被釋放,這個理解是正確的。
但是這種清緩存的工作也並不是沒有成本。理解cache是幹什麼的就可以明白清緩存必須保證cache中的數據跟對應文件中的數據一致,才能對cache進行釋放。
所以伴隨着cache清除的行爲的,一般都是系統IO飆高。因爲內核要對比cache中的數據和對應硬盤文件上的數據是否一致,如果不一致需要寫回,之後才能回收。
在系統中除了內存將被耗盡的時候可以清緩存以外,我們還可以使用下面這個文件來人工觸發緩存清除的操作:

/proc是一個虛擬文件系統,我們可以通過對它的讀寫操作做爲與kernel實體間進行通信的一種手段.也就是說可以通過修改/proc中的文件,來對當前kernel的行爲做出調整.那麼我們可以通過調整/proc/sys/vm/drop_caches來釋放內存.操作如下:


[root@localhost4 ~]# cat /proc/sys/vm/drop_caches
0

首先,/proc/sys/vm/drop_caches的值,默認爲0

這個文件可以設置的值分別爲1、2、3。它們所表示的含義爲:

echo 1 > /proc/sys/vm/drop_caches:表示清除page cache。

echo 2 > /proc/sys/vm/drop_caches:表示清除回收slab分配器中的對象(包括目錄項緩存和inode緩存)。slab分配器是內核中管理內存的一種機制,其中很多緩存數據實現都是用的pagecache。

echo 3 > /proc/sys/vm/drop_caches:表示清除page cache和slab分配器中的緩存對象。


[root@localhost4 ~]# echo 3 > /proc/sys/vm/drop_caches
[root@localhost4 ~]# free -m
             total       used       free     shared    buffers     cached
Mem:          3740        465       3274          0          0         29
-/+ buffers/cache:        435       3304
Swap:         3871          0       3871

再來運行free命令,buffers爲0MB,cached爲29MB.那麼有效的釋放了buffer和cache.


7. Linux SWAP使用情況

如果系統的物理內存用光了,則會用到swap。系統就會跑得很慢,但仍能運行;如果Swap空間用光了,那麼系統就會發生錯誤。通常會出現“application is out of memory”的錯誤,嚴重時會造成服務進程的死鎖。所以要高度重視。如果常常swap用很多,可能你就要考慮加物理內存了.這也是linux看內存是否夠用的標準。

我們通過free,top,cat /proc/swaps、swapon -s 等命令查看Swap分區的情況.

通過vmstat -n 1也能查看到當前系統使用內存的變化:


[root@localhost4 ~]# vmstat -n 1
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0 3350920   3632  30724    0    0    20     8   27   51  0  0 99  0  0
 0  0      0 3350912   3632  30776    0    0     0     0   96  166  0  0 100  0  0

Memory(內存):
swpd: 使用虛擬內存大小
free: 可用內存大小
buff: 用作緩衝的內存大小
cache: 用作緩存的內存大小

Swap:
si (swap in ): 每秒從交換區寫到內存的大小
so(swap out): 每秒寫入交換區的內存大小

在Linux內核 2.6.16中引入了一個系統內存接口特性,這個接口位於/proc/$pid/目錄下的smaps文件中 ,一看內容發現是進程內存映像信息,比同一目錄下的maps文件更詳細些。
cat /proc/1/smaps
ps -ef|grep nginx   
root      2164  2163  0 10:48 ?        00:00:00 nginx: master process 
www       2165  2164  0 10:48 ?        00:00:00 nginx: worker process

cat /proc/2164/smaps
 ...
7fffa8962000-7fffa8977000 rw-p 00000000 00:00 0                          [stack]
Size:                 88 kB
Rss:                  20 kB
Pss:                  12 kB
Shared_Clean:          0 kB
Shared_Dirty:          8 kB
Private_Clean:         0 kB
Private_Dirty:        12 kB
Referenced:           20 kB
Anonymous:            20 kB
AnonHugePages:         0 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB

解釋下samps裏面的內容:

7fffa8962000-7fffa8977000 是該虛擬內存段的開始和結束位置
rw-p 內存段的權限,rw是指可讀寫,p是指私有,如果是s則爲共享
00000000該虛擬內存段在對應的映射文件中的偏移量
00:00 文件的主設備和次設備號
0被映射到虛擬內存的文件的索引節點號
[stack] 被映射到虛擬內存的文件名稱
Size 是進程使用內存空間,並不一定實際分配了內存(VSS)
Rss是實際分配的內存(不需要缺頁中斷就可以使用的)
Shared_Clean 和其他進程共享的未改寫頁面
Shared_Dirty 和其他進程共享的已改寫頁面
Private_Clean 未改寫的私有頁面頁面
Private_Dirty 已改寫的私有頁面頁面
Swap 存在於交換分區的數據大小(如果物理內存有限,可能存在一部分在主存一部分在交換分區)
Pss是平攤計算後的使用內存(有些內存會和其他進程共享,例如mmap進來的)

           

分享一下我老師大神的人工智能教程。零基礎!通俗易懂!風趣幽默!還帶黃段子!希望你也加入到我們人工智能的隊伍中來!https://blog.csdn.net/jiangjunshow

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章