Linux 系统 /proc/meminfo 中的 DirectMap2M / DirectMap4k 是什么意思?

您所在的位置:网站首页 page_load代码什么意思 Linux 系统 /proc/meminfo 中的 DirectMap2M / DirectMap4k 是什么意思?

Linux 系统 /proc/meminfo 中的 DirectMap2M / DirectMap4k 是什么意思?

2023-03-26 22:08| 来源: 网络整理| 查看: 265

/PROC/MEMINFO之谜/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有什么联系和区别?很多细节在手册中并没有讲清楚,本文对此做了一点探究。

好文推荐:

秋招将至,关于Linux内核你掌握了多少?

玩转腾讯首发Linux内核源码《嵌入式开发笔记》,也许能帮到你哦

超细节!十年码农讲述Linux网络新技术基石——eBPF and XDP

最新干货!使用eBPF LSM热修复Linux内核漏洞

浅谈ARM64Linux内核页表的块映射

一、Linux内存总览图二、meminfo参数的详细介绍

/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中的内存初始化信息。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 allocatorkmalloc: 以字节为单位分配物理地址连续的内存块,它是以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了。

【文章福利】小编推荐自己的Linux内核源码交流群:【869634926】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!前50名可进群领取,并额外赠送一份价值600的内核资料包(含视频教程、电子书、实战项目及代码)!点击下方链接即可免费领取内核相关学习资料

学习直通车:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈

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记录看到:

# 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。

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 add_to_page_cache_lru

其它问题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中的缘故。

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)】

我爱内核网 - 构建全国最权威的内核技术交流分享论坛

原文链接:深度探究Linux内存中的/proc/meminfo参数

(版权归原作者所有,侵删)



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3