/proc/meminfo詳解

本文轉載自http://linuxperf.com/?p=142


/proc/meminfo是瞭解Linux系統內存使用狀況的主要接口,我們最常用的”free”、”vmstat”等命令就是通過它獲取數據的 ,/proc/meminfo所包含的信息比”free”等命令要豐富得多,然而真正理解它並不容易,比如我們知道”Cached”統計的是文件緩存頁,manpage上說是”In-memory  cache  for  files read from the disk (the page cache)”,那爲什麼它不等於[Active(file)+Inactive(file)]?AnonHugePages與AnonPages、HugePages_Total有什麼聯繫和區別?很多細節在手冊中並沒有講清楚,本文對此做了一點探究。


負責輸出/proc/meminfo的源代碼是: 
fs/proc/meminfo.c : meminfo_proc_show()

MemTotal:        3809036 kB
MemFree:          282012 kB
MemAvailable:     865620 kB
Buffers:               0 kB
Cached:           854972 kB
SwapCached:       130900 kB
Active:          1308168 kB
Inactive:        1758160 kB
Active(anon):    1010416 kB
Inactive(anon):  1370480 kB
Active(file):     297752 kB
Inactive(file):   387680 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:       4063228 kB
SwapFree:        3357108 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:       2104412 kB
Mapped:            40988 kB
Shmem:            169540 kB
Slab:             225420 kB
SReclaimable:     134220 kB
SUnreclaim:        91200 kB
KernelStack:        5936 kB
PageTables:        35628 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     5967744 kB
Committed_AS:    5626436 kB
VmallocTotal:   34359738367 kB
VmallocUsed:      351900 kB
VmallocChunk:   34359363652 kB
HardwareCorrupted:     0 kB
AnonHugePages:    139264 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:      204484 kB
DirectMap2M:     3915776 kB


MemTotal 

系統從加電開始到引導完成,firmware/BIOS要保留一些內存,kernel本身要佔用一些內存,最後剩下可供kernel支配的內存就是MemTotal。這個值在系統運行期間一般是固定不變的。可參閱 解讀DMESG中的內存初始化信息 http://ju.outofmemory.cn/entry/278827 


MemFree 

表示系統尚未使用的內存。(MemTotal-MemFree)就是已被用掉的內存。


MemAvailable 

有些應用程序會根據系統的可用內存大小自動調整內存申請的多少,所以需要一個記錄當前可用內存數量的統計值,MemFree並不適用,因爲MemFree不能代表全部可用的內存,系統中有些內存雖然已被使用但是可以回收的,比如cache/buffer、slab都有一部分可以回收,所以這部分可回收的內存加上MemFree纔是系統可用的內存,即MemAvailable。/proc/meminfo中的MemAvailable是內核使用特定的算法估算出來的,要注意這是一個估計值,並不精確。


內存黑洞 

追蹤Linux系統的內存使用一直是個難題,很多人試着把能想到的各種內存消耗都加在一起,kernel text、kernel modules、buffer、cache、slab、page table、process RSS…等等,卻總是與物理內存的大小對不上,這是爲什麼呢?因爲Linux kernel並沒有滴水不漏地統計所有的內存分配,kernel動態分配的內存中就有一部分沒有計入/proc/meminfo中。


我們知道,Kernel的動態內存分配通過以下幾種接口:

  • alloc_pages/__get_free_page: 以頁爲單位分配

  • vmalloc: 以字節爲單位分配虛擬地址連續的內存塊

  • slab allocator

    • kmalloc: 以字節爲單位分配物理地址連續的內存塊,它是以slab爲基礎的,使用slab層的general caches — 大小爲2^n,名稱是kmalloc-32、kmalloc-64等(在老kernel上的名稱是size-32、size-64等)。


通過slab層分配的內存會被精確統計,可以參見/proc/meminfo中的slab/SReclaimable/SUnreclaim;


通過vmalloc分配的內存也有統計,參見/proc/meminfo中的VmallocUsed 和 /proc/vmallocinfo(下節中還有詳述);


而通過alloc_pages分配的內存不會自動統計,除非調用alloc_pages的內核模塊或驅動程序主動進行統計,否則我們只能看到free memory減少了,但從/proc/meminfo中看不出它們具體用到哪裏去了。比如在VMware guest上有一個常見問題,就是VMWare ESX宿主機會通過guest上的Balloon driver(vmware_balloon module)佔用guest的內存,有時佔用得太多會導致guest無內存可用,這時去檢查guest的/proc/meminfo只看見MemFree很少、但看不出內存的去向,原因就是Balloon driver通過alloc_pages分配內存,沒有在/proc/meminfo中留下統計值,所以很難追蹤。


內存都到哪裏去了?

使用內存的,不是kernel就是用戶進程,下面我們就分類討論。

注:page cache比較特殊,很難區分是屬於kernel還是屬於進程,其中被進程mmap的頁面自然是屬於進程的了,而另一些頁面沒有被mapped到任何進程,那就只能算是屬於kernel了。


1. 內核

內核所用內存的靜態部分,比如內核代碼、頁描述符等數據在引導階段就分配掉了,並不計入MemTotal裏,而是算作Reserved(在dmesg中能看到)。而內核所用內存的動態部分,是通過上文提到的幾個接口申請的,其中通過alloc_pages申請的內存有可能未納入統計,就像黑洞一樣。

下面討論的都是/proc/meminfo中所統計的部分。


1.1 SLAB 

通過slab分配的內存被統計在以下三個值中:

  • SReclaimable: slab中可回收的部分。調用kmem_getpages()時加上SLAB_RECLAIM_ACCOUNT標記,表明是可回收的,計入SReclaimable,否則計入SUnreclaim。

  • SUnreclaim: slab中不可回收的部分。

  • Slab: slab中所有的內存,等於以上兩者之和。


1.2 VmallocUsed 

通過vmalloc分配的內存都統計在/proc/meminfo的 VmallocUsed 值中,但是要注意這個值不止包括了分配的物理內存,還統計了VM_IOREMAP、VM_MAP等操作的值,譬如VM_IOREMAP是把IO地址映射到內核空間、並未消耗物理內存,所以我們要把它們排除在外。從物理內存分配的角度,我們只關心VM_ALLOC操作,這可以從/proc/vmallocinfo中的vmalloc記錄看到:


注:/proc/vmallocinfo中能看到vmalloc來自哪個調用者(caller),那是vmalloc()記錄下來的,相應的源代碼可見: 
mm/vmalloc.c: vmalloc > __vmalloc_node_flags > __vmalloc_node > __vmalloc_node_range > __get_vm_area_node > setup_vmalloc_vm 


通過vmalloc分配了多少內存,可以統計/proc/vmallocinfo中的vmalloc記錄,例如:


一些driver以及網絡模塊和文件系統模塊可能會調用vmalloc,加載內核模塊(kernel module)時也會用到,可參見 kernel/module.c。


1.3 kernel modules (內核模塊) 

系統已經加載的內核模塊可以用 lsmod 命令查看,注意第二列就是內核模塊所佔內存的大小,通過它可以統計內核模塊所佔用的內存大小,但這並不準,因爲”lsmod”列出的是[init_size+core_size],而實際給kernel module分配的內存是以page爲單位的,不足 1 page的部分也會得到整個page,此外每個module還會分到一頁額外的guard page。下文我們還會細說。


lsmod的信息來自/proc/modules,它顯示的size包括init_size和core_size,相應的源代碼參見:


注:我們可以在 /sys/module/<module-name>/ 目錄下分別看到coresize和initsize的值。


kernel module的內存是通過vmalloc()分配的(參見下列源代碼),所以在/proc/vmallocinfo中會有記錄,也就是說我們可以不必通過”lsmod”命令來統計kernel module所佔的內存大小,通過/proc/vmallocinfo就行了,而且還比lsmod更準確,爲什麼這麼說呢?


因爲給kernel module分配內存是以page爲單位的,不足 1 page的部分也會得到整個page,此外,每個module還會分到一頁額外的guard page。 
詳見:mm/vmalloc.c: __get_vm_area_node()


而”lsmod”列出的是[init_size+core_size],比實際分配給kernel module的內存小。我們做個實驗來說明:


所以結論是kernel module所佔用的內存包含在/proc/vmallocinfo的統計之中,不必再去計算”lsmod”的結果了,而且”lsmod”也不準。


1.4 HardwareCorrupted 

當系統檢測到內存的硬件故障時,會把有問題的頁面刪除掉,不再使用,/proc/meminfo中的HardwareCorrupted統計了刪除掉的內存頁的總大小。相應的代碼參見 mm/memory-failure.c:memory_failure ()。


1.5 PageTables 

Page Table用於將內存的虛擬地址翻譯成物理地址,隨着內存地址分配得越來越多,Page Table會增大,/proc/meminfo中的PageTables統計了Page Table所佔用的內存大小。


注:請把Page Table與Page Frame(頁幀)區分開,物理內存的最小單位是page frame,每個物理頁對應一個描述符(struct page),在內核的引導階段就會分配好、保存在mem_map[]數組中,mem_map[]所佔用的內存被統計在dmesg顯示的reserved中,/proc/meminfo的MemTotal是不包含它們的。(在NUMA系統上可能會有多個mem_map數組,在node_data中或mem_section中)。 
而Page Table的用途是翻譯虛擬地址和物理地址,它是會動態變化的,要從MemTotal中消耗內存。


1.6 KernelStack 

每一個用戶線程都會分配一個kernel stack(內核棧),內核棧雖然屬於線程,但用戶態的代碼不能訪問,只有通過系統調用(syscall)、自陷(trap)或異常(exception)進入內核態的時候纔會用到,也就是說內核棧是給kernel code使用的。在x86系統上Linux的內核棧大小是固定的8K或16K(可參閱我以前的文章: 內核棧溢出 http://ju.outofmemory.cn/entry/254937 )。


Kernel stack(內核棧)是常駐內存的,既不包括在LRU lists裏,也不包括在進程的RSS/PSS內存裏,所以我們認爲它是kernel消耗的內存。統計值是/proc/meminfo的KernelStack。


1.7 Buffers 

Buffers統計的是直接訪問塊設備時的緩衝區的總大小,有時候對文件系統元數據的操作也會用到buffers。這部分內存不好直接對應到某個用戶進程,應該算作kernel佔用。


1.8 Bounce 

有些老設備只能訪問低端內存,比如16M以下的內存,當應用程序發出一個I/O 請求,DMA的目的地址卻是高端內存時(比如在16M以上),內核將在低端內存中分配一個臨時buffer作爲跳轉,把位於高端內存的緩存數據複製到此處。這種額外的數據拷貝被稱爲”bounce buffering”,會降低I/O 性能。大量分配的bounce buffers 也會佔用額外的內存。


2. 用戶進程

/proc/meminfo統計的是系統全局的內存使用狀況,單個進程的情況要看/proc/<pid>/下的smaps等等。


2.1 Hugepages 

Hugepages在/proc/meminfo中是被獨立統計的,與其它統計項不重疊,既不計入進程的RSS/PSS中,又不計入LRU Active/Inactive,也不會計入cache/buffer。如果進程使用了Hugepages,它的RSS/PSS不會增加。

注:不要把 Transparent HugePages (THP)跟 Hugepages 搞混了,THP的統計值是/proc/meminfo中的”AnonHugePages”,在/proc/<pid>/smaps中也有單個進程的統計,這個統計值與進程的RSS/PSS是有重疊的,如果用戶進程用到了THP,進程的RSS/PSS也會相應增加,這與Hugepages是不同的。


在/proc/meminfo中與Hugepages有關的統計值如下:


HugePages_Total 對應內核參數 vm.nr_hugepages,也可以在運行中的系統上直接修改 /proc/sys/vm/nr_hugepages,修改的結果會立即影響空閒內存 MemFree的大小,因爲HugePages在內核中獨立管理,只要一經定義,無論是否被使用,都不再屬於free memory。在下例中我們設置256MB(128頁)Hugepages,可以立即看到Memfree立即減少了262144kB(即256MB):


使用Hugepages有三種方式: 
(詳見 https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt )

  1. mount一個特殊的 hugetlbfs 文件系統,在上面創建文件,然後用mmap() 進行訪問,如果要用 read() 訪問也是可以的,但是 write() 不行。

  2. 通過shmget/shmat也可以使用Hugepages,調用shmget申請共享內存時要加上 SHM_HUGETLB 標誌。

  3. 通過 mmap(),調用時指定MAP_HUGETLB 標誌也可以使用Huagepages。


用戶程序在申請Hugepages的時候,其實是reserve了一塊內存,並未真正使用,此時/proc/meminfo中的 HugePages_Rsvd 會增加,而 HugePages_Free 不會減少。


等到用戶程序真正讀寫Hugepages的時候,它才被消耗掉了,此時HugePages_Free會減少,HugePages_Rsvd也會減少。


我們說過,Hugepages是獨立統計的,如果進程使用了Hugepages,它的RSS/PSS不會增加。下面舉例說明,一個進程通過mmap()申請並使用了Hugepages,在/proc/<pid>/smaps中可以看到如下內存段,VmFlags包含的”ht”表示Hugepages,kernelPageSize是2048kB,注意RSS/PSS都是0:



2.2 AnonHugePages 

AnonHugePages統計的是Transparent HugePages (THP),THP與Hugepages不是一回事,區別很大。


上一節說過,Hugepages在/proc/meminfo中是被獨立統計的,與其它統計項不重疊,既不計入進程的RSS/PSS中,又不計入LRU Active/Inactive,也不會計入cache/buffer。如果進程使用了Hugepages,它的RSS/PSS不會增加。


而AnonHugePages完全不同,它與/proc/meminfo的其他統計項有重疊,首先它被包含在AnonPages之中,而且在/proc/<pid>/smaps中也有單個進程的統計,與進程的RSS/PSS是有重疊的,如果用戶進程用到了THP,進程的RSS/PSS也會相應增加,這與Hugepages是不同的。下例截取自/proc/<pid>/smaps中的一段:


THP也可以用於shared memory和tmpfs,缺省是禁止的,打開的方法如下(詳見 https://www.kernel.org/doc/Documentation/vm/transhuge.txt):

  • mount時加上”huge=always”等選項

  • 通過/sys/kernel/mm/transparent_hugepage/shmem_enabled來控制


因爲缺省情況下shared memory和tmpfs不使用THP,所以進程之間不會共享AnonHugePages,於是就有以下等式: 
【/proc/meminfo的AnonHugePages】==【所有進程的/proc/<pid>/smaps中AnonHugePages之和】 
舉例如下:



2.3 LRU 

LRU是Kernel的頁面回收算法(Page Frame Reclaiming)使用的數據結構,在 解讀vmstat中的Active/Inactive memory 一文中有介紹。Page cache和所有用戶進程的內存(kernel stack和huge pages除外)都在LRU lists上。

LRU lists包括如下幾種,在/proc/meminfo中都有對應的統計值:

LRU_INACTIVE_ANON  –  對應 Inactive(anon) 
LRU_ACTIVE_ANON  –  對應 Active(anon) 
LRU_INACTIVE_FILE  –  對應 Inactive(file) 
LRU_ACTIVE_FILE  –  對應 Active(file) 
LRU_UNEVICTABLE  –  對應 Unevictable

注:

  • Inactive list裏的是長時間未被訪問過的內存頁,Active list裏的是最近被訪問過的內存頁,LRU算法利用Inactive list和Active list可以判斷哪些內存頁可以被優先回收。

  • 括號中的 anon 表示匿名頁(anonymous pages)。 
    用戶進程的內存頁分爲兩種:file-backed pages(與文件對應的內存頁),和anonymous pages(匿名頁),比如進程的代碼、映射的文件都是file-backed,而進程的堆、棧都是不與文件相對應的、就屬於匿名頁。file-backed pages在內存不足的時候可以直接寫回對應的硬盤文件裏,稱爲page-out,不需要用到交換區(swap);而anonymous pages在內存不足時就只能寫到硬盤上的交換區(swap)裏,稱爲swap-out。

  • 括號中的 file 表示 file-backed pages(與文件對應的內存頁)。

  • Unevictable LRU list上是不能pageout/swapout的內存頁,包括VM_LOCKED的內存頁、SHM_LOCK的共享內存頁(又被統計在”Mlocked”中)、和ramfs。在unevictable list出現之前,這些內存頁都在Active/Inactive lists上,vmscan每次都要掃過它們,但是又不能把它們pageout/swapout,這在大內存的系統上會嚴重影響性能,設計unevictable list的初衷就是避免這種情況,參見: 
    https://www.kernel.org/doc/Documentation/vm/unevictable-lru.txt 


LRU與/proc/meminfo中其他統計值的關係:

  • LRU中不包含HugePages_*。

  • LRU包含了 Cached 和 AnonPages。


2.4 Shmem 

/proc/meminfo中的Shmem統計的內容包括:

  • shared memory

  • tmpfs。


此處所講的shared memory又包括:

  • SysV shared memory [shmget etc.]

  • POSIX shared memory [shm_open etc.]

  • shared anonymous mmap [ mmap(…MAP_ANONYMOUS|MAP_SHARED…)]


因爲shared memory在內核中都是基於tmpfs實現的,參見: 
https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt 
也就是說它們被視爲基於tmpfs文件系統的內存頁,既然基於文件系統,就不算匿名頁,所以不被計入/proc/meminfo中的AnonPages,而是被統計進了:

  • Cached (i.e. page cache)

  • Mapped (當shmem被attached時候)


然而它們背後並不存在真正的硬盤文件,一旦內存不足的時候,它們是需要交換區才能swap-out的,所以在LRU lists裏,它們被放在:

  • Inactive(anon) 或 Active(anon) 
    注:雖然它們在LRU中被放進了anon list,但是不會被計入 AnonPages。這是shared memory & tmpfs比較擰巴的一個地方,需要特別注意。

  • 或 unevictable (如果被locked的話)


注意: 
當shmget/shm_open/mmap創建共享內存時,物理內存尚未分配,要直到真正訪問時才分配。/proc/meminfo中的 Shmem 統計的是已經分配的大小,而不是創建時申請的大小。


2.5 AnonPages 

前面提到用戶進程的內存頁分爲兩種:file-backed pages(與文件對應的內存頁),和anonymous pages(匿名頁)。Anonymous pages(匿名頁)的數量統計在/proc/meminfo的AnonPages中。

以下是幾個事實,有助於瞭解Anonymous Pages:

  • 所有page cache裏的頁面(Cached)都是file-backed pages,不是Anonymous Pages。 
    注:shared memory 不屬於 AnonPages,而是屬於Cached,因爲shared memory基於tmpfs,所以被視爲file-backed、在page cache裏,上一節解釋過。

  • mmap private anonymous pages屬於AnonPages(Anonymous Pages),而mmap sharedanonymous pages屬於Cached(file-backed pages),因爲shared anonymous mmap也是基於tmpfs的,上一節解釋過。

  • Anonymous Pages是與用戶進程共存的,一旦進程退出,則Anonymous pages也釋放,不像page cache即使文件與進程不關聯了還可以緩存。

  • AnonPages統計值中包含了Transparent HugePages (THP)對應的 AnonHugePages 。參見:



2.6 Mapped 

上面提到的用戶進程的file-backed pages就對應着/proc/meminfo中的”Mapped”。Page cache中(“Cached”)包含了文件的緩存頁,其中有些文件當前已不在使用,page cache仍然可能保留着它們的緩存頁面;而另一些文件正被用戶進程關聯,比如shared libraries、可執行程序的文件、mmap的文件等,這些文件的緩存頁就稱爲mapped。


/proc/meminfo中的”Mapped”就統計了page cache(“Cached”)中所有的mapped頁面。


因爲Linux系統上shared memory & tmpfs被計入page cache(“Cached”),所以被attached的shared memory、以及tmpfs上被map的文件都算做”Mapped”。

進程所佔的內存頁分爲anonymous pages和file-backed pages,理論上應該有: 
【所有進程的PSS之和】 == 【Mapped + AnonPages】。 
然而我實際測試的結果,雖然兩者很接近,卻總是無法精確相等,我猜也許是因爲進程始終在變化、採集的/proc/[1-9]*/smaps以及/proc/meminfo其實不是來自同一個時間點的緣故。


2.7 Cached 

Page Cache裏包括所有file-backed pages,統計在/proc/meminfo的”Cached”中。

  • Cached不僅包括mapped,也包括unmapped的頁面,當一個文件不再與進程關聯之後,原來在page cache中的頁面並不會立即回收,仍然被計入Cached,還留在LRU中,但是 Mapped 統計值會減小。【ummaped = (Cached – Mapped)】

  • Cached包含tmpfs中的文件,POSIX/SysV shared memory,以及shared anonymous mmap。 
    注:POSIX/SysV shared memory和shared anonymous mmap在內核中都是基於tmpfs實現的,參見: 
    https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt 


有意思的是,Shared memory和tmpfs在不發生swap-out的時候屬於”Cached”,而在swap-out/swap-in的過程中會被加進swap cache中、屬於”SwapCached”,一旦進了”SwapCached”,就不再屬於”Cached”了。”Cached”和”SwapCached”兩個統計值是互不重疊的,源代碼參見下一節。


2.8 SwapCached 

我們說過,匿名頁(anonymous pages)要用到交換區,而shared memory和tmpfs雖然未統計在AnonPages裏,但它們背後沒有硬盤文件,所以也是需要交換區的。也就是說需要用到交換區的內存包括:”AnonPages”和”Shmem”,我們姑且把它們統稱爲匿名頁好了。


交換區可以包括一個或多個交換區設備(裸盤、邏輯卷、文件都可以充當交換區設備),每一個交換區設備都對應自己的swap cache,可以把swap cache理解爲交換區設備的”page cache”:page cache對應的是一個個文件,swap cache對應的是一個個交換區設備,kernel管理swap cache與管理page cache一樣,用的都是radix-tree,唯一的區別是:page cache與文件的對應關係在打開文件時就確定了,而一個匿名頁只有在即將被swap-out的時候才決定它會被放到哪一個交換區設備,即匿名頁與swap cache的對應關係在即將被swap-out時才確立。


並不是每一個匿名頁都在swap cache中,只有以下情形之一的匿名頁纔在:

  • 匿名頁即將被swap-out時會先被放進swap cache,但通常只存在很短暫的時間,因爲緊接着在pageout完成之後它就會從swap cache中刪除,畢竟swap-out的目的就是爲了騰出空閒內存; 
    【注:參見mm/vmscan.c: shrink_page_list(),它調用的add_to_swap()會把swap cache頁面標記成dirty,然後它調用try_to_unmap()將頁面對應的page table mapping都刪除,再調用pageout()回寫dirty page,最後try_to_free_swap()會把該頁從swap cache中刪除。】

  • 曾經被swap-out現在又被swap-in的匿名頁會在swap cache中,直到頁面中的內容發生變化、或者原來用過的交換區空間被回收爲止。 
    【注:當匿名頁的內容發生變化時會刪除對應的swap cache,代碼參見mm/swapfile.c: reuse_swap_page()。】


/proc/meminfo中的SwapCached背後的含義是:系統中有多少匿名頁曾經被swap-out、現在又被swap-in並且swap-in之後頁面中的內容一直沒發生變化。也就是說,如果這些匿名頁需要被swap-out的話,是無需進行I/O write操作的。


“SwapCached”不屬於”Cached”,兩者沒有交叉。參見:


“SwapCached”內存同時也在LRU中,還在”AnonPages”或”Shmem”中,它本身並不佔用額外的內存。


2.9 Mlocked 

“Mlocked”統計的是被mlock()系統調用鎖定的內存大小。被鎖定的內存因爲不能pageout/swapout,會從Active/Inactive LRU list移到Unevictable LRU list上。也就是說,當”Mlocked”增加時,”Unevictable”也同步增加,而”Active”或”Inactive”同時減小;當”Mlocked”減小的時候,”Unevictable”也同步減小,而”Active”或”Inactive”同時增加。


“Mlocked”並不是獨立的內存空間,它與以下統計項重疊:LRU Unevictable,AnonPages,Shmem,Mapped等。


其它問題


DirectMap 

/proc/meminfo中的DirectMap所統計的不是關於內存的使用,而是一個反映TLB效率的指標。TLB(Translation Lookaside Buffer)是位於CPU上的緩存,用於將內存的虛擬地址翻譯成物理地址,由於TLB的大小有限,不能緩存的地址就需要訪問內存裏的page table來進行翻譯,速度慢很多。爲了儘可能地將地址放進TLB緩存,新的CPU硬件支持比4k更大的頁面從而達到減少地址數量的目的, 比如2MB,4MB,甚至1GB的內存頁,視不同的硬件而定。”DirectMap4k”表示映射爲4kB的內存數量, “DirectMap2M”表示映射爲2MB的內存數量,以此類推。所以DirectMap其實是一個反映TLB效率的指標。


Dirty pages到底有多少? 

/proc/meminfo 中有一個Dirty統計值,但是它未能包括系統中全部的dirty pages,應該再加上另外兩項:NFS_Unstable 和 Writeback,NFS_Unstable是發給NFS server但尚未寫入硬盤的緩存頁,Writeback是正準備回寫硬盤的緩存頁。即:

系統中全部dirty pages = ( Dirty + NFS_Unstable + Writeback )

注1:NFS_Unstable的內存被包含在Slab中,因爲nfs request內存是調用kmem_cache_zalloc()申請的。

注2:anonymous pages不屬於dirty pages。 
參見mm/vmscan.c: page_check_dirty_writeback() 
“Anonymous pages are not handled by flushers and must be written from reclaim context.” 


爲什麼【Active(anon)+Inactive(anon)】不等於AnonPages? 

因爲Shared memory & tmpfs 被計入LRU Active/Inactive(anon),但未計入 AnonPages。所以一個更合理的等式是:


【Active(anon)+Inactive(anon)】 = 【AnonPages + Shmem】

但是這個等式在某些情況下也不一定成立,因爲:

  • 如果shmem或anonymous pages被mlock的話,就不在Active(non)或Inactive(anon)裏了,而是到了Unevictable裏,以上等式就不平衡了;

  • 當anonymous pages準備被swap-out時,分幾個步驟:先被加進swap cache,再離開AnonPages,然後離開LRU Inactive(anon),最後從swap cache中刪除,這幾個步驟之間會有間隔,而且有可能離開AnonPages就因某些情況而結束了,所以在某些時刻以上等式會不平衡。 
    【注:參見mm/vmscan.c: shrink_page_list(): 
    它調用的add_to_swap()會把swap cache頁面標記成dirty,然後調用try_to_unmap()將頁面對應的page table mapping都刪除,再調用pageout()回寫dirty page,最後try_to_free_swap()把該頁從swap cache中刪除。】


爲什麼【Active(file)+Inactive(file)】不等於Mapped? 
  1. 因爲LRU Active(anon)和Inactive(anon)中包含unmapped頁面;

  2. Mapped中包含shared memory & tmpfs,這部分內存被計入了LRU Active(anon)或Inactive(anon)、而不在Active(file)和Inactive(file)中。


同理:【Active(file)+Inactive(file)】!= Cached 
因爲shared memory & tmpfs包含在Cached中,而不在Active(file)和Inactive(file)中。


如果不考慮mlock添亂的話,一個更符合邏輯的等式是: 
【Active(file) + Inactive(file) + Shmem】== Cached 


注: 
測試的結果以上等式通常都成立,但內存發生交換的時候以上等式有時不平衡,我猜可能是因爲有些屬於Shmem的內存swap-out的過程中離開Cached進入了Swapcached,但沒有立即從swap cache刪除、仍算在Shmem中的緣故。


Linux的內存都用到哪裏去了?

儘管不可能精確統計Linux系統的內存,但大體瞭解還是可以的。

kernel內存的統計方式應該比較明確,即 

【Slab+ VmallocUsed + PageTables + KernelStack + Buffers + HardwareCorrupted + Bounce + X】

  • 注1:VmallocUsed其實不是我們感興趣的,因爲它還包括了VM_IOREMAP等並未消耗物理內存的IO地址映射空間,我們只關心VM_ALLOC操作,(參見1.2節),所以實際上應該統計/proc/vmallocinfo中的vmalloc記錄,例如(此處單位是byte):


  • 注2:kernel module的內存被包含在VmallocUsed中,見1.3節。

  • 注3:X表示直接通過alloc_pages/__get_free_page分配的內存,沒有在/proc/meminfo中統計,不知道有多少,就像個黑洞。


用戶進程的內存主要有三種統計口徑: 
  • [1]圍繞LRU進行統計 
    【(Active + Inactive + Unevictable) + (HugePages_Total * Hugepagesize)】

  • [2]圍繞Page Cache進行統計 
    當SwapCached爲0的時候,用戶進程的內存總計如下: 
    【(Cached + AnonPages) + (HugePages_Total * Hugepagesize)】 
    當SwapCached不爲0的時候,以上公式不成立,因爲SwapCached可能會含有Shmem,而Shmem本來被含在Cached中,一旦swap-out就從Cached轉移到了SwapCached,可是我們又不能把SwapCached加進上述公式中,因爲SwapCached雖然不與Cached重疊卻與AnonPages有重疊,它既可能含有Shared memory又可能含有Anonymous Pages。

  • [3]圍繞RSS/PSS進行統計 
    把/proc/[1-9]*/smaps 中的 Pss 累加起來就是所有用戶進程佔用的內存,但是還沒有包括Page Cache中unmapped部分、以及HugePages,所以公式如下: 
    ΣPss + (Cached – mapped) + (HugePages_Total * Hugepagesize)


所以系統內存的使用情況可以用以下公式表示: 
  • MemTotal = MemFree +【Slab+ VmallocUsed + PageTables + KernelStack + Buffers + HardwareCorrupted + Bounce + X】+【Active + Inactive + Unevictable + (HugePages_Total * Hugepagesize)】

  • MemTotal = MemFree +【Slab+ VmallocUsed + PageTables + KernelStack + Buffers + HardwareCorrupted + Bounce + X】+【Cached + AnonPages + (HugePages_Total * Hugepagesize)】

  • MemTotal = MemFree +【Slab+ VmallocUsed + PageTables + KernelStack + Buffers + HardwareCorrupted + Bounce + X】+【ΣPss + (Cached – mapped) + (HugePages_Total * Hugepagesize)】

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