這篇博客主要介紹 linux 環境下,查看內存占用的兩種方式:使用 ps,top等命令;查看/proc/[pid]/下的文件。文章簡要介紹了命令的使用方法與一些參數意義,同時對/proc/[pid]/下的文件內容進行了一些詳細的介紹。文章內容來自google和自我總結,如有不當之處,歡迎批評指正。
查看Linux內存的方法
linux 下面查看內存有多種渠道,比如通過命令 ps ,top,free, pmap 等,或者通過/proc系統。一般情況下,ps,top,pmap,free可以滿足要求,如果需要比較詳細和精確地知道整機內存或者某個進程內存的使用情況,可以通過/proc 系統。
使用命令
free : 顯示系統可用內存以及已經使用的內存的信息
ps: 查看進程信息,靜態,即當前狀態
top: 查看進程信息,動態
pstree: 查看進程樹
pmap: 根據進程ID查看進程信息
ps vs top
- ps命令–提供系統過去信息的一次性快照,也就是說ps命令能夠查看剛剛系統的進程信息。
- top命令反應的是系統進程動態信息,默認10s更新一次。
- ps和top都是從/proc目錄下讀取進程的狀態信息,內核把當前系統進程的各種有用信息都放在這個偽目錄下。
常見ps命令:
ps -aux: 查看系統所有進程
ps -l: 進查看自己的bash相關進程
top 命令詳解,請參考http://www.jb51.net/article/40807.htm
基本命令:
- 按鍵盤數字“1”,可監控每個邏輯CPU的狀況:
- 鍵盤“b”(打開/關閉加亮效果)
- 鍵盤“x”(打開/關閉排序列的加亮效果)
- ”shift + >”或”shift + <”可以向右或左改變排序列
- 敲擊“f”鍵,編排基本視圖中的顯示字段
- s,改變畫面更新頻率
- l,關閉或開啟第一部分第一行 top 信息的表示
- t,關閉或開啟第一部分第二行 Tasks 和第三行 Cpus 信息的表示
- m,關閉或開啟第一部分第四行 Mem 和 第五行 Swap 信息的表示
具體使用方法,可以使用 man [cmd] 查看。
相關參數說明
VSZ & VIRT
- 進程使用的虛擬內存值總量,包括所有代碼,數據,共享庫已經被swApped out的。VIRT = SWAP + RES。
- VSZ來自ps命令, VIRT來自top命令,二者均表示進程占用的虛擬內存大小。
- 假如進程申請100m的內存,但實際只使用了10m,那么它會增長100m,而不是實際的使用量
RES & RSS
- 進程當前使用的內存大小,但不包括swap out. RES = CODE +DATA。
- 包含其他進程的共享
- RES 來自 top 命令, RSS 來自 ps 命令,兩者在表示意義上沒有區別,都是從 /cat/proc/[pid]/stat 文件中讀取的信息。
- 如果申請100m的內存,實際使用10m,它只增長10m,與VIRT相反
- 關于庫占用內存的情況,它只統計加載的庫文件所占內存大小
CODE
可執行代碼占用的物理內存大小
DATA
- 物理內存中存放數據的大小,在程序運行中需要用到
- 如果top命令沒有顯示, 按f鍵顯示
SHR
- 共享內存大小
- 除了自身進程的共享內存,也包含其他進程的共享內存
- 計算某個進程所占用物理內存的大?。?RES - SHR
- swap out后,該值會下降。
查看一個進程的內存信息步驟
1.獲取進程PID
$ ps -aux | grep /usr/sbin/NetworkManager root 845 0.0 0.0 387084 13332 ? Ssl 3月28 0:00 /usr/sbin/NetworkManager --no-daemon
2.查看進程的所有線程
$ ps mp 845 -o THREAD,tid USER %CPU PRI SCNT WCHAN USER SYSTEM TID root 0.0 - - - - - - root 0.0 19 - - - - 845 root 0.0 19 - - - - 1025 root 0.0 19 - - - - 1027
3.查看所有子進程
$ pstree -p 845 NetworkManager(845)─┬─dhclient(30278) ├─DNSmasq(1123) ├─{gdbus}(1027) └─{gmain}(1025)
使用 /proc 下文件
/proc/[pid]/ 下面與進程內存相關的文件主要有maps , smaps, status。
maps: 文件可以查看某個進程的代碼段、棧區、堆區、動態庫、內核區對應的虛擬地址
smaps: 顯示每個分區更詳細的內存占用數據
status: 包含了所有CPU活躍的信息,該文件中的所有值都是從系統啟動開始累計到當前時刻
有名與匿名:
一個文件可以映射到進程的一段內存區域中,映射的文件描述符保存在vm_area_struct->vm_file域中,這種內存區域叫做有名內存區域,相反,屬于匿名映射內存區域。
maps 文件分析
Proc/[pid]/maps 顯示進程映射了的內存區域和訪問權限。對應內核中的操作集為 proc_pid_maps_op,具體的導出函數為 show_map 。內核中進程的一段地址空間用一個vm_area_struct結構體表示,所有地址空間存儲在task->mm->mmap鏈表中。
截取一行內容如下:
7f4e3f5ca000-7f4e3f674000 r-xp 00000000 08:02 525202 /usr/lib/x86_64-linux-gnu/NetworkManager/libnm-device-plugin-wifi.so
Vm_area_struct每項對應解析如下表所示:
maps文件只能顯示簡單的分區,smap文件可以顯示每個分區的更詳細的內存占用數據。
smap 文件分析
截取一段文件,各字段解析如下:
7f148b2fa000-7f148b2fb000 rw-p 00026000 08:02 2883675 /lib/x86_64-linux-gnu/ld-2.23.so Size: 4 kB 虛擬內存大小 Rss: 4 kB 實際使用物理內存大小 RSS = Shared_Clean+Shared_Dirty+Private_Clean+Private_Dirty Pss: 4 kB RSS中私有的內存頁面 Shared_Clean: 0 kB RSS中共享內存,沒有被改寫的頁面 Shared_Dirty: 0 kB RSS中共享內存,被改寫的頁面 Private_Clean: 0 kB RSS中私有內存,未被改寫 Private_Dirty: 4 kB RSS中私有內存,被改寫 Referenced: 4 kB Anonymous: 4 kB AnonHugePages: 0 kB Shared_Hugetlb: 0 kB Private_Hugetlb: 0 kB Swap: 0 kB 處于交換區的頁面大小 SwapPss: 0 kB KernelPageSize: 4 kB 操作系統一個頁面大小 MMUPageSize: 4 kB 體系結構MMU一個頁面大小 Locked: 0 kB VmFlags: rd wr mr mw me dw ac sd
Dirty頁面如果沒有交換機制的情況下,應該是不能回收的。
分析腳本:
自己寫了個簡單的分析腳本,如下,可以根據需要進行修改。
#! /bin/bash awk 'BEGIN{ total = 0; printf("SIZEtRSStSHARED_CLEANtSHARED_DIRTYtPRIVATE_CLEANtPRIVATE_DIRTYn") }{ if(NF >3){ if($2 ~ /[r-][w-][x-][ps]/){ if($6 =="") name = $1; else name = $6; } } while(getline) { if(NF >3){ if($2 ~ /[r-][w-][x-][ps]/){ if($6 =="") name = $1; else name = $6; } } if($1 ~ /^Size/){ size = $2; total += $2; } if($1 ~ /Rss/){ rss = $2; } if($1 ~ /Shared_Clean/){ shared_clean = $2; } if($1 ~ /Shared_Dirty/){ shared_dirty = $2; } if($1 ~ /Private_Clean/){ private_clean = $2; } if($1 ~ /Private_Dirty/){ private_dirty = $2; } if($1 ~ /VmFlags/){ printf("%dt%dt%dt%dt%dt%dt%sn",size,rss,shared_clean,shared_dirty,private_clean,private_dirty,name); size = 0; name = ""; rss = 0; shared_clean = 0; shared_dirty = 0; private_clean = 0; private_dirty = 0; continue; } } }END{ printf("====total: %dn", total); }' $1
關于匿名映射
smaps 中可能會存在大量的匿名區域,它們是使用 mmap 機制生成的,但是沒有關聯到任何一個文件。通常情況下,它們主要用于處理一些瑣碎的任務,比如處理沒有在堆中申請的共享內存或者緩沖區。比如 pthread 使用匿名映射區作為新線程的棧,在linux 下,pthread 為 新線程申請 8M 空間作為??臻g 已經一個小小片內存(如4Kb)用于檢測內存溢出。所以在每個pthread創建時,會分配一個映射到節點0的8Mb內存,和一個映射到節點0的4Kb區域。
status 文件分析
截取文件,解析個字段如下:
Develop>cat /proc/24475/status Name: netio 可執行程序的名字 State: R (running) 任務狀態,運行/睡眠/僵死 Tgid: 24475 線程組號 Pid: 24475 進程id PPid: 19635 父進程id TracerPid: 0 Uid: 0 0 0 0 Gid: 0 0 0 0 FDSize: 256 該進程最大文件描述符個數 Groups: 0 VmPeak: 6330708 kB 內存使用峰值 VmSize: 268876 kB 進程虛擬地址空間大小 VmLck: 0 kB 進程鎖住的物理內存大小,鎖住的物理內存無法交換到硬盤 VmHWM: 16656 kB VmRSS: 11420 kB 進程正在使用的物理內存大小 VmData: 230844 kB 進程數據段大小 VmStk: 136 kB 進程用戶態棧大小 VmExe: 760 kB 進程代碼段大小 VmLib: 7772 kB 進程使用的庫映射到虛擬內存空間的大小 VmPTE: 120 kB 進程頁表大小 VmSwap: 0 kB Threads: 5 共享使用該信號描述符的任務的個數,在POSIX多線程序應用程序中,線程組中的所有線程使用同一個信號描述符。 SigQ: 0/63346 待處理信號的個數 SigPnd: 0000000000000000 屏蔽位,存儲了該線程的待處理信號 ShdPnd: 0000000000000000 屏蔽位,存儲了該線程組的待處理信號 SigBlk: 0000000000000000 存放被阻塞的信號 SigIgn: 0000000001000000 存放被忽略的信號 SigCgt: 0000000180000000 存放被俘獲到的信號 CapInh: 0000000000000000 能被當前進程執行的程序的繼承的能力 CapPrm: ffffffffffffffff 進程能夠使用的能力,可以包含CapEff中沒有的能力,這些能力是被進程自己臨時放棄的,CapEff是CapPrm的一個子集,進程放棄沒有必要的能力有利于提高安全性 CapEff: ffffffffffffffff 進程的有效能力 CapBnd: ffffffffffffffff Cpus_allowed: 01 Cpus_allowed_list: 0 Mems_allowed: 01 Mems_allowed_list: 0 voluntary_ctxt_switches: 201 nonvoluntary_ctxt_switches: 909
meminfo 文件分析
整機內存使用情況的文件/proc/meminfo,摘取分析如下
Develop>cat /proc/meminfo MemTotal: 8112280 kB 所有可用RAM大小 (即物理內存減去一些預留位和內核的二進制代碼大?。?MemFree: 4188636 kB LowFree與HighFree的總和,被系統留著未使用的內存 Buffers: 34728 kB 用來給文件做緩沖大小 Cached: 289740 kB 被高速緩沖存儲器(cache memory)用的內存的大小 (等于 diskcache minus SwapCache ) SwapCached: 0 kB 被高速緩沖存儲器(cache memory)用的交換空間的大小 已經被交換出來的內存,但仍然被存放在swapfile中。 用來在需要的時候很快的被替換而不需要再次打開I/O端口 Active: 435240 kB 在活躍使用中的緩沖或高速緩沖存儲器頁面文件的大小, 除非非常必要否則不會被移作他用 Inactive: 231512 kB 在不經常使用中的緩沖或高速緩沖存儲器頁面文件的大小,可能被用于其他途徑. Active(anon): 361252 kB Inactive(anon): 120688 kB Active(file): 73988 kB Inactive(file): 110824 kB Unevictable: 0 kB Mlocked: 0 kB SwapTotal: 0 kB 交換空間的總大小 SwapFree: 0 kB 未被使用交換空間的大小 Dirty: 0 kB 等待被寫回到磁盤的內存大小 Writeback: 0 kB 正在被寫回到磁盤的內存大小 AnonPages: 348408 kB 未映射頁的內存大小 Mapped: 33600 kB 已經被設備和文件等映射的大小 Shmem: 133536 kB Slab: 55984 kB 內核數據結構緩存的大小,可以減少申請和釋放內存帶來的消耗 SReclaimable: 25028 kB 可收回Slab的大小 SUnreclaim: 30956 kB 不可收回Slab的大?。⊿Unreclaim+SReclaimable=Slab) KernelStack: 1896 kB 內核棧區大小 PageTables: 8156 kB 管理內存分頁頁面的索引表的大小 NFS_Unstable: 0 kB 不穩定頁表的大小 Bounce: 0 kB WritebackTmp: 0 kB CommitLimit: 2483276 kB Committed_AS: 1804104 kB VmallocTotal: 34359738367 kB 可以vmalloc虛擬內存大小 VmallocUsed: 565680 kB 已經被使用的虛擬內存大小 VmallocChunk: 34359162876 kB HardwareCorrupted: 0 kB HugePages_Total: 1536 大頁面數目 HugePages_Free: 0 空閑大頁面數目 HugePages_Rsvd: 0 HugePages_Surp: 0 Hugepagesize: 2048 kB 大頁面一頁大小 DirectMap4k: 10240 kB DirectMap2M: 8302592 kB
總結
linux下內存占用是一個比較復雜的概念,不能簡單通過一個單一指標就判斷某個程序“內存消耗”大小,因為進程所申請的內存不一定真正會被用到(malloc或mmap的實現)而且真正用到了的內存也不一定是只有該進程自己在用 (比如動態共享庫)。我們應該根據具體需求選擇合適的方式去分析內存占用。