Linux 内存详解,即top、free、vmstat、meminfo、Buffer和Cache的介绍 | 您所在的位置:网站首页 › cache也是内存的一部分嘛为什么 › Linux 内存详解,即top、free、vmstat、meminfo、Buffer和Cache的介绍 |
温故而知新,可以为师矣。
1、top linux下的任务管理器
top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器。top是一个动态显示过程,即可以通过用户按键来不断刷新当前状态.如果在前台执行该命令,它将独占前台,直到用户终止该程序为止.比较准确的说,top命令提供了实时的对系统处理器的状态监视.它将显示系统中CPU最“敏感”的任务列表.该命令可以按CPU使用.内存使用和执行时间对任务进行排序;而且该命令的很多特性都可以通过交互式命令或者在个人定制文件中进行设定。
$top
top - 09:14:56 up 264 days, 20:56, 1 user, load average: 0.02, 0.04, 0.00
Tasks: 87 total, 1 running, 86 sleeping, 0 stopped, 0 zombie
Cpu(s): 0.0%us, 0.2%sy, 0.0%ni, 99.7%id, 0.0%wa, 0.0%hi, 0.0%si, 0.2%st
Mem: 377672k total, 322332k used, 55340k free, 32592k buffers
Swap: 397308k total, 67192k used, 330116k free, 71900k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 2856 656 388 S 0.0 0.2 0:49.40 init
2 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kthreadd
3 root 20 0 0 0 0 S 0.0 0.0 7:15.20 ksoftirqd/0
4 root RT 0 0 0 0 S 0.0 0.0 0:00.00 migration/0
第一行
09:14:56 : 系统当前时间
264 days, 20:56 : 系统开机到现在经过了多少时间
1 users : 当前2用户在线
load average: 0.02, 0.04, 0.00: 系统1分钟、5分钟、15分钟的CPU负载信息
第二行
Tasks:任务;
87 total:很好理解,就是当前有87个任务,也就是87个进程。
1 running:1个进程正在运行
86 sleeping:86个进程睡眠
0 stopped:停止的进程数
0 zombie:僵死的进程数
第三行
Cpu(s):表示这一行显示CPU总体信息
0.0%us:用户态进程占用CPU时间百分比,不包含renice值为负的任务占用的CPU的时间。
0.7%sy:内核占用CPU时间百分比
0.0%ni:改变过优先级的进程占用CPU的百分比
99.3%id:空闲CPU时间百分比
0.0%wa:等待I/O的CPU时间百分比
0.0%hi:CPU硬中断时间百分比
0.0%si:CPU软中断时间百分比
注:这里显示数据是所有cpu的平均值,如果想看每一个cpu的处理情况,按1即可;折叠,再次按1;
第四行
Men:内存的意思
8175320kk total:物理内存总量
8058868k used:使用的物理内存量
116452k free:空闲的物理内存量
283084k buffers:用作内核缓存的物理内存量
第五行
Swap:交换空间
6881272k total:交换区总量
4010444k used:使用的交换区量
2870828k free:空闲的交换区量
4336992k cached:缓冲交换区总量
进程信息
再下面就是进程信息:
PID:进程的ID
USER:进程所有者
PR:进程的优先级别,越小越优先被执行
NInice:值
VIRT:进程占用的虚拟内存
RES: 进程占用的物理内存
SHR:进程使用的共享内存
S:进程的状态。S表示休眠,R表示正在运行,Z表示僵死状态,N表示该进程优先值为负数
%CPU:进程占用CPU的使用率
%MEM:进程使用的物理内存和总内存的百分比
TIME+:该进程启动后占用的总的CPU时间,即占用CPU使用时间的累加值。
COMMAND:进程启动命令名称
top命令交互操作指令
下面列出一些常用的 top命令操作指令
q:退出top命令
:立即刷新
s:设置刷新时间间隔
c:显示命令完全模式
t::显示或隐藏进程和CPU状态信息
m:显示或隐藏内存状态信息
l:显示或隐藏uptime信息
f:增加或减少进程显示标志
S:累计模式,会把已完成或退出的子进程占用的CPU时间累计到父进程的MITE+
P:按%CPU使用率排行
T:按MITE+排行
M:按%MEM排行
e: 调整内存显示单位m/G
u:指定显示用户进程
r:修改进程renice值
kkill:进程
i:只显示正在运行的进程
W:保存对top的设置到文件^/.toprc,下次启动将自动调用toprc文件的设置。
h:帮助命令。
q:退出
注:强调一下,使用频率最高的是P、T、M,因为通常使用top,我们就想看看是哪些进程最耗cpu资源、占用的内存最多; 注:通过”shift + >”或”shift + /
proc
/
sys
/
vm
/
drop_caches
读一个大文件,并记录时间;
关闭该文件;
重读这个大文件,并记录时间;
第二次读应该比第一次快很多。原来我做过一个BerkeleyDB的读操作,大概要读5G的文件,几千万条记录。在我的环境上,第二次读比第一次大概可以快9倍左右。
free输出的第二行是从一个应用程序的角度看系统内存的使用情况。
对于FO[3][2],即-buffers/cache,表示一个应用程序认为系统被用掉多少内存;
对于FO[3][3],即+buffers/cache,表示一个应用程序认为系统还有多少内存;
因为被系统cache和buffer占用的内存可以被快速回收,所以通常FO[3][3]比FO[2][3]会大很多。
3.Buffer和Cache介绍
MemTotal 系统从加电开始到引导完成, f irmware/BIOS要保留一些内存,kernel本身要占用一些内存,最后剩下可供kernel支配的内存就是MemTotal 。这个值在系统运行期间一般是固定不变的。可参阅 解读DMESG中的内存初始化信息 。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等)。 (1)通过slab层分配的内存会被精确统计,可以参见/proc/meminfo中的slab/SReclaimable/SUnreclaim; (2)通过vmalloc分配的内存也有统计,参见/proc/meminfo中的VmallocUsed 和 /proc/vmallocinfo(下节中还有详述); (3)而 通过 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中留下统计值,所以很难追踪。 6. 内存都到哪里去了? 使用内存的,不是kernel就是用户进程,下面我们就分类讨论。 注:page cache比较特殊,很难区分是属于kernel还是属于进程,其中被进程mmap的页面自然是属于进程的了,而 另一些页面没有被mapped到任何进程,那就只能算是属于kernel了 。 6.1. 内核 内核所用内存的静态部分,比如内核代码、页描述符等数据在引导阶段就分配掉了,并不计入MemTotal里,而是算作Reserved(在dmesg中能看到)。而内核所用内存的动态部分,是通过上文提到的几个接口申请的,其中通过alloc_pages申请的内存有可能未纳入统计,就像黑洞一样。 下面讨论的都是/proc/meminfo中所统计的部分。 6.1.1 SLAB 通过slab分配的内存被统计在以下三个值中: SReclaimable: slab中可回收的部分。调用kmem_getpages()时加上SLAB_RECLAIM_ACCOUNT标记,表明是可回收的,计入SReclaimable,否则计入SUnreclaim。 SUnreclaim: slab中不可回收的部分。 Slab: slab中所有的内存,等于以上两者之和。 6.1.2 VmallocUsed 通过vmalloc分配的内存都统计在/proc/meminfo的 VmallocUsed 值中,但是要注意这个值不止包括了分配的物理内存,还统计了VM_IOREMAP、VM_MAP等操作的值,譬如VM_IOREMAP是把IO地址映射到内核空间、并未消耗物理内存,所以我们要把它们排除在外。 从物理内存分配的角度,我们只关心VM_ALLOC操作,这可以从/proc/vmallocinfo中的vmalloc记录看到: # grep vmalloc /proc/vmallocinfo . . . 0xffffc90004702000 - 0xffffc9000470b000 36864 alloc_large_system_hash + 0x171 / 0x239 pages = 8 vmalloc N0 = 8 0xffffc9000470b000 - 0xffffc90004710000 20480 agp_add_bridge + 0x2aa / 0x440 pages = 4 vmalloc N0 = 4 0xffffc90004710000 - 0xffffc90004731000 135168 raw_init + 0x41 / 0x141 pages = 32 vmalloc N0 = 32 0xffffc90004736000 - 0xffffc9000473f000 36864 drm_ht_create + 0x55 / 0x80 [ drm ] pages = 8 vmalloc N0 = 8 0xffffc90004744000 - 0xffffc90004746000 8192 dm_table_create + 0x9e / 0x130 [ dm_mod ] pages = 1 vmalloc N0 = 1 0xffffc90004746000 - 0xffffc90004748000 8192 dm_table_create + 0x9e / 0x130 [ dm_mod ] pages = 1 vmalloc N0 = 1 . . . 注:/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记录,例如: # grep vmalloc /proc/vmallocinfo | awk '{total+=$2}; END {print total}' 23375872 一些driver以及网络模块和文件系统模块可能会调用vmalloc,加载内核模块(kernel module)时也会用到,可参见 kernel/module.c。 6.1.3 kernel modules (内核模块) 系统已经加载的内核模块可以用 lsmod 命令查看,注意第二列就是内核模块所占内存的大小,通过它可以统计内核模块所占用的内存大小,但这并不准,因为”lsmod”列出的是[init_size+core_size],而实际给kernel module分配的内存是以page为单位的,不足 1 page的部分也会得到整个page,此外每个module还会分到一页额外的guard page。下文我们还会细说。 # lsmod | less Module Size Used by rpcsec_gss _krb5 31477 0 auth _rpcgss 59343 1 rpcsec_gss_krb5 nfsv4 474429 0 dns _resolver 13140 1 nfsv4 nfs 246411 1 nfsv4 lockd 93977 1 nfs sunrpc 295293 5 nfs , rpcsec_gss_krb5 , auth_rpcgss , lockd , nfsv4 fscache 57813 2 nfs , nfsv4 . . . lsmod的信息来自/proc/modules,它显示的size包括init_size和core_size,相应的源代码参见: // kernel/module.c static int m_show ( struct seq_file * m , void * p ) { . . . seq_printf ( m , "%s %u" , mod -> name , mod -> init_size + mod -> core_size ) ; . . . } 注:我们可以在 /sys/module// 目录下分别看到coresize和initsize的值。 kernel module的内存是通过vmalloc()分配的(参见下列源代码),所以在/proc/vmallocinfo中会有记录,也就是说我们可以不必通过”lsmod”命令来统计kernel module所占的内存大小,通过/proc/vmallocinfo就行了,而且还比lsmod更准确,为什么这么说呢? // kernel/module.c static int move_module ( struct module * mod , struct load_info * info ) { . . . ptr = module_alloc_update_bounds ( mod -> core_size ) ; . . . if ( mod -> init_size ) { ptr = module_alloc_update_bounds ( mod -> init_size ) ; . . . } // 注:module_alloc_update_bounds()最终会调用vmalloc_exec() 因为给kernel module分配内存是以page为单位的,不足 1 page的部分也会得到整个page,此外,每个module还会分到一页额外的guard page。 详见:mm/vmalloc.c: __get_vm_area_node() 而”lsmod”列出的是[init_size+core_size],比实际分配给kernel module的内存小。我们做个实验来说明: # 先卸载floppy模块 $ modprobe - r floppy # 确认floppy模块已经不在了 $ lsmod | grep floppy # 记录vmallocinfo以供随后比较 $ cat / proc / vmallocinfo > vmallocinfo . 1 # 加载floppy模块 $ modprobe - a floppy # 注意floppy模块的大小是69417字节: $ lsmod | grep floppy floppy 69417 0 $ cat / proc / vmallocinfo > vmallocinfo . 2 # 然而,我们看到vmallocinfo中记录的是分配了73728字节: $ diff vmallocinfo . 1 vmallocinfo . 2 68a69 > 0xffffffffa03d7000 - 0xffffffffa03e9000 73728 module_alloc_update_bounds + 0x14 / 0x70 pages = 17 vmalloc N0 = 17 # 为什么lsmod看到的内存大小与vmallocinfo不同呢? # 因为给kernel module分配内存是以page为单位的,而且外加一个guard page # 我们来验证一下: $ bc - q 69417 % 4096 3881 . . . cached = global_page_state ( NR_FILE_PAGES ) - total_swapcache_pages ( ) - i . bufferram ; . . . } “SwapCached”内存同时也在LRU中,还在”AnonPages”或”Shmem”中,它本身并不占用额外的内存。 6.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等。 6.2.10 Buffers “Buffers”表示块设备(block device)所占用的缓存页,包括:直接读写块设备、以及文件系统元数据(metadata)比如SuperBlock所使用的缓存页。它与“Cached”的区别在于,”Cached”表示普通文件所占用的缓存页。参见我的另一篇文章 http://linuxperf.com/?p=32 “Buffers”所占的内存同时也在LRU list中,被统计在Active(file)或Inactive(file)。注:通过阅读源代码可知,块设备的读写操作涉及的缓存被纳入了LRU,以读操作为例,do_generic_file_read()函数通过 mapping->a_ops->readpage() 调用块设备底层的函数,并调用 add_to_page_cache_lru() 把缓存页加入到LRU list中。参见: filemap.c: do_generic_file_read > add_to_page_cache_lru 7. 其它问题 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? 因为Shmem(即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? 因为LRU Active(file)和Inactive(file)中不仅包含mapped页面,还包含unmapped页面; Mapped中包含”Shmem”(即shared memory & tmpfs),这部分内存被计入了LRU Active(anon)或Inactive(anon)、而不在Active(file)和Inactive(file)中。为什么【Active(file)+Inactive(file)】不等于 Cached? 因为”Shmem”(即shared memory & tmpfs)包含在Cached中,而不在Active(file)和Inactive(file)中; Active(file)和Inactive(file)还包含Buffers。 如果不考虑mlock的话,一个更符合逻辑的等式是: 【Active(file) + Inactive(file) + Shmem】== 【Cached + Buffers】 如果有mlock的话,等式应该如下(mlock包括file和anon两部分,/proc/meminfo中并未分开统计,下面的mlock_file只是用来表意,实际并没有这个统计值): 【Active(file) + Inactive(file) + Shmem + mlock_file】== 【Cached + Buffers】注: 测试的结果以上等式通常都成立,但内存发生交换的时候以上等式有时不平衡,我猜可能是因为有些属于Shmem的内存swap-out的过程中离开Cached进入了Swapcached,但没有立即从swap cache删除、仍算在Shmem中的缘故。 8. Linux的内存都用到哪里去了? 尽管不可能精确统计Linux系统的内存,但大体了解还是可以的。 kernel内存的统计方式应该比较明确,即 【Slab+ VmallocUsed + PageTables + KernelStack + HardwareCorrupted + Bounce + X】 注1:VmallocUsed其实不是我们感兴趣的,因为它还包括了VM_IOREMAP等并未消耗物理内存的IO地址映射空间,我们只关心VM_ALLOC操作,(参见1.2节),所以实际上应该统计/proc/vmallocinfo中的vmalloc记录,例如(此处单位是byte): # grep vmalloc /proc/vmallocinfo | awk '{total+=$2}; END {print total}' 23375872 注2:kernel module的内存被包含在VmallocUsed中,见1.3节。 注3:X表示直接通过alloc_pages/__get_free_page分配的内存,没有在/proc/meminfo中统计,不知道有多少,就像个黑洞。用户进程的内存主要有三种统计口径: 围绕LRU进行统计 【(Active + Inactive + Unevictable) + (HugePages_Total * Hugepagesize)】 围绕Page Cache进行统计 当SwapCached为0的时候,用户进程的内存总计如下: 【(Cached + AnonPages + Buffers) + (HugePages_Total * Hugepagesize)】 当SwapCached不为0的时候,以上公式不成立,因为SwapCached可能会含有Shmem,而Shmem本来被含在Cached中,一旦swap-out就从Cached转移到了SwapCached,可是我们又不能把SwapCached加进上述公式中,因为SwapCached虽然不与Cached重叠却与AnonPages有重叠,它既可能含有Shared memory又可能含有Anonymous Pages。 围绕RSS/PSS进行统计 把/proc/[1-9]*/smaps 中的 Pss 累加起来就是所有用户进程占用的内存,但是还没有包括Page Cache中unmapped部分、以及HugePages,所以公式如下: ΣPss + (Cached – mapped) + Buffers + (HugePages_Total * Hugepagesize)所以系统内存的使用情况可以用以下公式表示: MemTotal = MemFree +【Slab+ VmallocUsed + PageTables + KernelStack + HardwareCorrupted + Bounce + X】+【Active + Inactive + Unevictable + (HugePages_Total * Hugepagesize)】 MemTotal = MemFree +【Slab+ VmallocUsed + PageTables + KernelStack + HardwareCorrupted + Bounce + X】+【Cached + AnonPages + Buffers + (HugePages_Total * Hugepagesize)】 MemTotal = MemFree +【Slab+ VmallocUsed + PageTables + KernelStack + HardwareCorrupted + Bounce + X】+【ΣPss + (Cached – mapped) + Buffers + (HugePages_Total * Hugepagesize)】 |
CopyRight 2018-2019 实验室设备网 版权所有 |