/proc/pids/smaps

Linux內存管理 -- /proc/{pid}/smaps講解

基本介紹

/proc/PID/smaps 文件是基於 /proc/PID/maps 的擴展,他展示了一個進程的內存消耗,比同一目錄下的maps文件更爲詳細。

值得說明一下的是,每一個VMA(虛擬內存區域,即一個 vm_area_struct 結構指向的內存區域)都有如下的一系列數據:

08048000-080bc000 r-xp 00000000 03:02 13130      /bin/bash
 
Size:               1084 kB
Rss:                 892 kB
Pss:                 374 kB
Shared_Clean:        892 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         0 kB
Referenced:          892 kB
Anonymous:             0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Locked:                0 kB
THPeligible:           0
VmFlags: rd ex mr mw me dw

第一行基礎信息

在講解字段含義之前,我們必須知道什麼匿名映射:

在Linux 內存管理的進程用戶態內存分佈中提到過,映射分爲文件映射和匿名映射。

文件映射就是磁盤中的數據通過文件系統映射到內存再通過文件映射映射到虛擬空間,這樣,用戶就可以在用戶空間通過 open ,read, write 等函數區操作文件內容。至於實際的代碼,open,read,write,close,mmap... 操作的虛擬地址都屬於文件映射。

匿名映射就是用戶空間需要分配一定的物理內存來存儲數據,這部分內存不屬於任何文件,內核就使用匿名映射將內存中的某段物理地址與用戶空間一一映射,這樣用戶就可用直接操作虛擬地址來範圍這段物理內存。比如使用malloc申請內存。

  • 08048000-080bc000 是該虛擬內存段的開始和結束位置
  • r-xp 內存段的權限,分別是可讀、可寫、可運行、私有或共享,最後一位p代表私有,s代表共享
  • 00000000 該虛擬內存段起始地址在對應的映射文件中以頁爲單位的偏移量,對匿名映射,它等於0或者vm_start/PAGE_SIZE
  • 03:02 文件的主設備號和次設備號。對匿名映射來說,因爲沒有文件在磁盤上,所以沒有設備號,始終爲00:00。對有名映射來說,是映射的文件所在設備的設備號。
  • 13130 被映射到虛擬內存的文件的索引節點號,通過該節點可以找到對應的文件,對匿名映射來說,因爲沒有文件在磁盤上,所以沒有節點號,始終爲00:00。
  • /bin/bash 被映射到虛擬內存的文件名稱。後面帶(deleted)的是內存數據,可以被銷燬。對有名來說,是映射的文件名。對匿名映射來說,是此段虛擬內存在進程中的角色。[stack]表示在進程中作爲棧使用,[heap]表示堆。其餘情況則無顯示。

第一行的信息完全同於在maps文件中輸出的信息。對於不熟悉maps文件的讀者可以先了解maps的字段的含義與實現機制。

詳細信息

Size:虛擬內存空間大小。但是這個內存值不一定是物理內存實際分配的大小,因爲在用戶態上,虛擬內存總是延遲分配的。這個值計算也非常簡單,就是該VMA的開始位置減結束位置。

延遲分配就是當進程申請內存的時候,Linux會給他先分配頁,但是並不會區建立頁與頁框的映射關係,意思就是說並不會分配物理內存,而當真正使用的時候,就會產生一個缺頁異常,硬件跳轉page fault處理程序執行,在其中分配物理內存,然後修改頁表(創建頁表項)。異常處理完畢,返回程序用戶態,繼續執行。

Rss:是實際分配的內存,這部分物理內存已經分配,不需要缺頁中斷就可以使用的。

這裏有一個公式計算Rss:
Rss=Shared_Clean+Shared_Dirty+Private_Clean+Private_Dirty
  • share/private:該頁面是共享還是私有。
  • dirty/clean:該頁面是否被修改過,如果修改過(dirty),在頁面被淘汰的時候,就會把該髒頁面回寫到交換分區(換出,swap out)。有一個標誌位用於表示頁面是否dirty。
  • share/private_dirty/clean 計算邏輯:
    查看該page的引用數,如果引用>1,則歸爲shared,如果是1,則歸爲private,同時也查看該page的flag,是否標記爲_PAGE_DIRTY,如果不是,則認爲乾淨的。

Pss(proportional set size):是平攤計算後的實際物理使用內存(有些內存會和其他進程共享,例如mmap進來的)。實際上包含下面private_clean+private_dirty,和按比例均分的shared_clean、shared_dirty。

舉個計算Pss的例子:
如果進程A有x個private_clean頁面,有y個private_dirty頁面,有z個shared_clean僅和進程B共享,有h個shared_dirty頁面和進程B、C共享。那麼進程A的Pss爲:

x + y + z/2 + h/3

Referenced:當前頁面被標記爲已引用或者包含匿名映射(The amount of memory currently marked as referenced or a mapping associated with a file may contain anonymous pages)。

Anonymous:匿名映射的物理內存,這部分內存不來自於文件的內存大小。

ShmemPmdMapped:PMD頁面已經被映射的共享(shmem / tmpfs)內存量。在官方文檔中,這樣解釋:"ShmemPmdMapped" shows the ammount of shared (shmem/tmpfs) memory backed by huge pages.

Shared/Private_Hugetlb:由hugetlbfs頁面支持的內存使用量,由於歷史原因,該頁面未計入“ RSS”或“ PSS”字段中。 並且這些沒有包含在Shared/Private_Clean/Dirty 字段中。

Swap:存在於交換分區的數據大小(如果物理內存有限,可能存在一部分在主存一部分在交換分區)

SwapPss:這個我並沒有找到對應解釋,但從源碼可以得知,計算邏輯就跟pss一樣,只不過針對的是交換分區的內存。

static void smaps_pte_entry(pte_t *pte, unsigned long addr,
        struct mm_walk *walk)
{
    struct mem_size_stats *mss = walk->private;
    struct vm_area_struct *vma = walk->vma;
    struct page *page = NULL;

    if (pte_present(*pte)) {//----------------------------------頁面在內存中
        page = vm_normal_page(vma, addr, *pte);
    } else if (is_swap_pte(*pte)) {//---------------------------頁面被swap出
        swp_entry_t swpent = pte_to_swp_entry(*pte);

        if (!non_swap_entry(swpent)) {
            int mapcount;

            mss->swap += PAGE_SIZE;
            mapcount = swp_swapcount(swpent);
            if (mapcount >= 2) {
                u64 pss_delta = (u64)PAGE_SIZE << PSS_SHIFT;
               do_div(pss_delta, mapcount);
                mss->swap_pss += pss_delta; // --------- 如果引用超過1,就將均值加入swap_pss中
            } else {
                mss->swap_pss += (u64)PAGE_SIZE << PSS_SHIFT;// ------------ 直接加一個頁大小
            }
       } else if (is_migration_entry(swpent))
            page = migration_entry_to_page(swpent);
    }

    if (!page)//----------------------------------------------如果頁面不存在,就不用更新mss其他信息了;如果存在,調用smaps_account()更新mss。
        return;
smaps_account(mss, page, PAGE_SIZE, pte_young(*pte), pte_dirty(*pte));
}

KernelPageSize:內核一頁的大小
MMUPageSize:MMU頁大小,大多數情況下,和KernelPageSize大小一樣。

Locked:常駐物理內存的大小,這些頁不會被換出。

THPeligible:映射是否符合分配THP的條件。如果爲true,則爲1,否則爲0。 它僅顯示當前狀態。

THP,透明大頁(Transparent Huge Pages),RHEL 6 開始引入,目的是使用更大的內存頁面(memory page size) 以適應越來越大的系統內存,讓操作系統可以支持現代硬件架構的大頁面容量功能。與標準大頁的區別在於分配機制,標準大頁管理是預分配的方式,而透明大頁管理則是動態分配的方式。

VmFlags:表示與特定虛擬內存區域關聯的內核標誌。標誌如下:

rd  - readable
wr  - writeable
ex  - executable
sh  - shared
mr  - may read
mw  - may write
me  - may execute
ms  - may share
gd  - stack segment growns down
pf  - pure PFN range
dw  - disabled write to the mapped file
lo  - pages are locked in memory
io  - memory mapped I/O area
sr  - sequential read advise provided
rr  - random read advise provided
dc  - do not copy area on fork
de  - do not expand area on remapping
ac  - area is accountable
nr  - swap space is not reserved for the area
ht  - area uses huge tlb pages
ar  - architecture specific flag
dd  - do not include area into core dump
sd  - soft-dirty flag
mm  - mixed map area
hg  - huge page advise flag
nh  - no-huge page advise flag
mg  - mergable advise flag
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章