内存池 linux,linux查看运行内存命令
很多朋友对于内存池 linux和linux查看运行内存命令不太懂,今天就由小编来为大家分享,希望可以帮助到大家,下面一起来看看吧!
深入理解Linux内存管理(十)meminfo详解
深入理解Linux内存管理(十)meminfo详解
前情回顾
前面一系列文章介绍了Linux内存管理大致框架,包括伙伴系统、slab、大页等等基本内容。本章节再深入介绍一个对了解系统内存使用很重要的文件,这就是meminfo。本章节会展开讲述meminfo每个字段的含义,以及彼此的关系。
问题探索
带着上面两个问题,我们开始探索meminfo的内容。
MemTotal
可以看出MemTotal其实就是_totalram_pages的值,有三个接口可以改变该值,有如下几种场景会修改该值:memblock释放、保留空间释放。这部分内存主要包括两部分,一部分是memblock释放的,21345页;另一部分是保留空间,占2138页。因此,这个虚拟机上一共占23483页(93932 kB),这个与meminfo查询到的数值是一致的。所以,此处可以回答第一个问题,就是操作系统、rootfs会保留一定的内存,这部分内存是不计算到MemTotal的(其实MemTotal的内存是伙伴系统能管理到的内存)。
MemFree
MemFree就是所有zone的free pages之和,但需要注意的是,这里包括based和global的free pages,但不包含cma区间的空闲内存。基于此,这个值是如下几部分之和:3945+ 0+ 6976+ 4* 4kB= 43700kB,这个和meminfo字段是吻合的。
MemAvailable
MemAvailable是一个预测值,预估系统还能使用的物理内存。在这个虚拟机上,该值的计算过程如下:可用物理内存(MemTotal- MemFree)- Buffers- Cached。总结:MemAvailable更多是一个预估值,并不能非常准确反映系统真正可用的内存。但为什么MemAvailable不包含LRU中匿名页,目前还不得而知。(初步理解是内存中的文件页都可以被回收?而未释放的匿名页的回收则需借助swap分区?不是所有系统都会挂载swap分区。)
Buffers
Buffers用于缓存硬盘文件的数据,包括从硬盘读取到的文件或者将要回写到磁盘的数据。
Cached
Cached与Buffers类似,其用于缓存内存文件的数据,包括从内存读取文件或者将数据写入内存文件中。这里需要注意的是:所有(文件)pagecache包括三部分,分别是Buffers+ Cached+ SwapCached。
SwapCached
SwapCached是内存与交换区设备的”中间层“,用于匿名页换入换出时的缓存。匿名页在放入到交换区之前,会在SwapCached中建立一个文件页,但该缓存很快就被刷入到交换区设备,然后从SwapCached中删除;另外,当匿名页从交换区设备换入内存时,会放入到SwapCached中。
LRU相关
Linux通过LRU数据结构管理已经被申请的页框(不包括大页内存),分为Active+ Inactive+ Unevictable三部分,其中Active表示的是“最近”使用过的页框,而Inactive则是“最近”没有使用过的页框,Unevictable表示被“锁”在内存的页框,这部分页框一帮情况下不能被回收。其中“最近”的意思是指当系统前一次LRU扫描时,到本次扫描的间隙。
Mlocked
当页框被内核mlock时,会统计到Mlocked项中,并且从Active/Inactive LRU链表中移动到Unevictable LRU链表中。
Swap相关
SwapTotal表示系统swap空间总大小,SwapFree是swap分区剩余大小。
Dirty
统计的是脏文件页,这些页在回收前,需要先刷入到磁盘中。需要注意的是,匿名页在swap-out前也会被置为脏页。
Writeback
统计正在执行回写操作的页。
AnonPages
统计的是用户进程正在使用的匿名页。与Cached统计不重叠。
Mapped
统计的是用户进程正在使用的文件页。与Cached统计有重叠,当进程被回收时,其对应的so、可执行程序、mmap的文件等不再统计到Mapped中,但可能还会统计到Cached中。
结合AnonPages,系统所有进程的PSS内存之和应该等于AnonPages+ Mapped。
Shmem
包括共享内存、tmpfs文件系统、devtmpfs文件系统的内容。内存文件系统中的文件,由于不是直接与硬盘映射,所以对其进行换入换出时需要swap分区,这行为和匿名页很类似,故这些页也被放入到Active/Inactive LRU链表中。
Shmem与Mapped、Cached统计有重叠,与AnonPages没有重叠。
KReclaimable
部分内核态可被回收的内存,包括slab和非slab可回收内核页。计算MemAvailable时使用了这部分内存。
Slab相关
Slab表示用于slab的所有内存,SReclaimable表示Slab中可被回收的内存,SUnreclaim表示Slab中不可以被回收的内存。
KernelStack
内核栈内存,包括内核态自身使用的栈空间,线程的内核栈空间,这部分内存不计算到LRU中,也不包括进程的PSS内存中。
PageTables
专用于页表的内存。
NFS_Unstable
默认是0,着实奇怪~
Bounce
适配老设备的内存,部分老设备只能访问低16M内存,如果超过这部分的内存,则需要中间区跳转,Bounce就是这个用途。
Commit相关
CommitLimit表示允许最大承诺可使用的内存,Committed_AS表示已经为承诺内存分配物理页的数量。嵌入式设备通常通过这两个值来衡量设备剩余可用内存。
Vmalloc相关
VmallocTotal是内核态预留的虚拟地址,在64位系统默认是32TB;VmallocUsed则是通过vmalloc接口分配的内存,这对应部分物理内存,以及VM_IOREMAP、VM_MAP等映射IO地址到内核空间的内存,这部分只占用虚拟内存,并不占用实际的物理内存。VmallocChunk则默认是0。
Percpu
统计的是percpu分配的内存数。
大页相关
AnonHugePages:用户态透明大页中的匿名页。进程使用透明大页时,在RSS/PSS中会统计,这点和标准大页不同。
ShmemHugePages:用于共享内存或tmpfs的透明大页,Linux 4.8加入内核。
ShmemPmdMapped:用户态共享内存映射的透明大页,Linux 4.8加入内核。
FileHugePages:与AnonHugePages对应,用户态透明大页中的文件页,
FilePmdMapped:用户态文件页映射的透明大页。
HugePages_Total:内存池中标准大页总数。
HugePages_Free:内存池用标准大页空闲数,包括承诺分配但未最终分配的页框。
HugePages_Rsvd:内存池承诺分配的大页数,所以内存池中真正空闲的页框数应该是HugePages_Free- HugePages_Rsvd。
HugePages_Surp:内存池超额分配的大页数,通过nr_overcommit_hugepages设置最大允许超额分配数量。
Hugepagesize:默认大页的大小。
Hugetlb:大页内存池页框总大小。
DirectMap
表示虚拟内存直接映射4k物理页和2M物理页的数量。
总结
在介绍完meminfo内容后,我们来解答第二个问题,就是MemTotal一共包含那几部分?其实无非就是几部分,伙伴系统剩余+内核态使用+用户态使用三部分。其中伙伴系统剩余就是MemFree,内核态和用户态则比较负责,下面分别阐述。
内核态部分比较固定,包括Slab、内核栈、vmalloc部分等等。
所有进程的PSS累加计算的是用户进程占用的内存,还有进程打开的内存文件(Cached- Mapped)以及打开的磁盘文件缓存(Buffers),最后就是大页部分的内存。
参考:1./PROC/MEMINFO之谜
2. proc(5)— Linux manual page
深度解读 Linux 内核级通用内存池 —— kmalloc 体系
本文深入解析 Linux内核的通用内存池体系,特别是 kmalloc体系。kmalloc体系主要针对内核中频繁需要的通用小内存块提供高效分配与释放服务。与专门针对特定数据结构的 slab内存池不同,kmalloc体系通过预先创建多个特定尺寸的 slab cache来应对不同大小的通用内存块需求。
kmalloc体系的底层基石基于 slab alloactor体系构建,通过 kmem_cache_create函数创建不同尺寸的通用 slab cache。本文详细介绍了 kmalloc内存池中可能包含的尺寸范围以及为何内核会特别支持 96字节和 192字节的通用 slab cache,以优化内存分配效率。
kmalloc内存池体系能够支持的内存块尺寸由 kmalloc_info[]数组定义,数组中的每个元素对应一个特定尺寸的 slab cache,范围从 8字节到 64M,包括两个特殊尺寸 96字节和 192字节。内核通过 size_index[]数组和 fls函数选择最适合申请内存块尺寸的 slab cache。
在 kmalloc体系中,内存块的分配与释放通过 slab cache来管理。分配过程考虑了内存块尺寸与 slab cache的匹配,而释放过程则确保内存块返回到正确的 slab cache中。通过 cat/proc/slabinfo命令,用户可以查看系统中不同尺寸的通用 slab cache。
本文进一步解释了 kmalloc体系的创建流程,包括初始化 kmalloc_caches二维数组以及创建具体尺寸的 slab cache。同时,kmalloc体系提供了 kfree函数用于内存块的释放,并通过 virt_to_head_page函数定位物理内存页,进一步管理内存释放过程。
kmalloc体系的核心是围绕 kmalloc_caches数组展开的,它定义了内存来源类型和内存块尺寸范围。通过 kmalloc_type函数,可以从用户指定的 gfp_t flags标记中提取出 kmalloc_cache_type。本文深入探讨了 kmalloc体系的内存分配与回收机制,以及如何选择合适的 slab cache来满足内存需求。
总之,kmalloc体系在 Linux内核中扮演着关键角色,通过高效的内存管理机制优化了内核的性能。本文旨在提供对 kmalloc体系的全面理解,帮助开发者和研究者深入理解 Linux内核中这一重要组件。
内存池的基本概念
内存池则是在真正使用内存之前,先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是,使得内存分配效率得到提升。
在内核中有不少地方内存分配不允许失败.作为一个在这些情况下确保分配的方式,内核开发者创建了一个已知为内存池(或者是 mempool)的抽象.一个内存池真实地只是一类后备缓存,它尽力一直保持一个空闲内存列表给紧急时使用.
一个内存池有一个类型 mempool_t(在<linux/mempool.h>中定义);你可以使用 mempool_create创建一个:
mempool_t*mempool_create(int min_nr, mempool_alloc_t*alloc_fn, mempool_free_t*free_fn, void*pool_data); min_nr参数是内存池应当一直保留的最小数量的分配的对象.实际的分配和释放对象由 alloc_fn和 free_fn处理,它们有这些原型:
typedef void*(mempool_alloc_t)(int gfp_mask, void*pool_data); typedef void(mempool_free_t)(void*element, void*pool_data);给 mempool_create最后的参数( pool_data)被传递给 alloc_fn和 free_fn.
如果需要,你可编写特殊用途的函数来处理 mempool的内存分配.常常,但是,你只需要使内核 slab分配器为你处理这个任务.有 2个函数( mempool_alloc_slab和 mempool_free_slab)来进行在内存池分配原型和 kmem_cache_alloc和 kmem_cache_free之间的感应淬火.因此,设置内存池的代码常常看来如此:
cache= kmem_cache_create(...); pool= mempool_create(MY_POOL_MINIMUM,mempool_alloc_slab, mempool_free_slab, cache);一旦已创建了内存池,可以分配和释放对象,使用:
void*mempool_alloc(mempool_t*pool, int gfp_mask); void mempool_free(void*element, mempool_t*pool);当内存池创建了,分配函数将被调用足够的次数来创建一个预先分配的对象池.因此,对 mempool_alloc的调用试图从分配函数请求额外的对象;如果那个分配失败,一个预先分配的对象(如果有剩下的)被返回.当一个对象被用 mempool_free释放,它保留在池中,如果对齐预分配的对象数目小于最小量;否则,它将被返回给系统.
一个 mempool可被重新定大小,使用:
int mempool_resize(mempool_t*pool, int new_min_nr, int gfp_mask);这个调用,如果成功,调整内存池的大小至少有 new_min_nr个对象.如果你不再需要一个内存池,返回给系统使用:
void mempool_destroy(mempool_t*pool);你编写返回所有的分配的对象,在销毁 mempool之前,否则会产生一个内核 oops.
如果你考虑在你的驱动中使用一个 mempool,请记住一件事: mempools分配一块内存在一个链表中,对任何真实的使用是空闲和无用的.容易使用 mempools消耗大量的内存.在几乎每个情况下,首选的可选项是不使用 mempool并且代替以简单处理分配失败的可能性.如果你的驱动有任何方法以不危害到系统完整性的方式来响应一个分配失败,就这样做.驱动代码中的 mempools的使用应当少.