Linux内存管理暴涨的问题学习

一、问题
近期我们运维同事接到线上 lb(负载均衡)服务内存报警,运维同事反馈说 lb 集群有部分机器的内存使用率超过 80%,有的甚至超过 90%,而且内存使用率还再不停的增长。接到内存报警的消息,让整个团队都比较紧张,我们团队负责的 lb 服务是零售、物流、科技等业务服务的流量入口,承接上万个服务的流量转发,一旦有故障影响业务服务比较多,必须马上着手解决内存暴涨的问题。目前只是内存报警,暂时不影响业务,先将内存使用率 90% 以上的 lb 服务下线,防止内存过高导致 lb 服务崩溃,影响业务,运维同事密切关注相关的内存报警的消息。
二、排查过程
经过开发同学通过 cat /proc/meminfo 查看 slab 的内核内存可能有泄漏。
$ cat /proc/meminfomemtotal: 65922868 kbmemfree: 9001452 kb...slab: 39242216 kbsreclaimable: 38506072 kbsunreclaim: 736144 kb....  
通过 slabtop 命令分析 slab 发现内核中 dentry 对象占比高,考虑到 dentry 对象跟文件有关,linux 中一切皆可以为文件,这个可能跟 socket 文件有关,通过进一步排查发现 lb 服务上有个 curl 发送的 https 探测脚本,这个脚本存在 dentry 对象泄漏,并且在 curl 论坛上找到一篇文章确认了这个问题,这个文章说明了 curl-7.19.7 版本在发送 https 请求时,curl 依赖的 nss 库存在 dentry 泄漏的 bug,我查看一下我们 curl 版本就是 7.19.7,问题终于真相大白了!!!
$ curl -vcurl 7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 nss/3.15.3 zlib/1.2.3 libidn/1.18 libssh2/1.4.2protocols: tftp ftp telnet dict ldap ldaps http file https ftps scp sftpfeatures: gss-negotiate idn ipv6 largefile ntlm ssl libz$ rpm -aq|grep nss-nss-util-3.16.1-3.el6.x86_64nss-sysinit-3.16.1-14.el6.x86_64nss-softokn-freebl-3.14.3-17.el6.x86_64nss-softokn-3.14.3-17.el6.x86_64nss-3.16.1-14.el6.x86_64nss-tools-3.16.1-14.el6.x86_64  
文章中介绍可以设置环境变量 nss_sdb_use_cache 修复这个 bug,我们验证通过了这个解决方案。
三、解决方案
1、目前先将探测脚本停止,在业务流量低峰时将内存使用率超过 90% 的服务先通过 drop_caches 清理一下缓存。
2、等大促过后,探测脚本中设置环境变量 nss_sdb_use_cache,彻底修复这个问题。
四、复盘和总结
这次内存暴涨的问题根本原因是 curl-7.19.7 依赖的 nss 库存在 dentry 泄漏的 bug 导致的,探测脚本只是将这个问题暴露出来。这次问题由 linux 内存泄漏引发的问题,因此以点带面再次系统学习一下 linux 内存管理的知识非常有必要,对我们以后排查内存暴涨的问题非常有帮助。
1)linux 内存寻址
linux 内核主要通过虚拟内存管理进程的地址空间,内核进程和用户进程都只会分配虚拟内存,不会分配物理内存,通过内存寻址将虚拟内存与物理内存做映射。linux 内核中有三种地址,
a、逻辑地址,每个逻辑地址都由一段 (segment) 和偏移量 (offset) 组成,偏移量指明了从段开始的地方到实际地址之间的距离。
b、线性地址,又称虚拟地址,是一个 32 个无符号整数,32 位机器内存高达 4gb,通常用十六进制数字表示,linux 进程的内存一般说的都是这个内存。
c、物理地址,用于内存芯片级内存单元寻址。它们与从 cpu 的地址引脚发送到内存总线上的电信号对应。
linux 中的内存控制单元 (mmu) 通过一种称为分段单元 (segmentation unit) 的硬件电路把一个逻辑地址转换成线性地址,接着,第二个称为分页单元 (paging unit) 的硬件电路把线性地址转换成一个物理地址。
2)linux 分页机制
分页单元把线性地址转换成物理地址。线性地址被分成以固定长度为单位的组,称为页 (page)。页内部连续的线性地址被映射到连续的物理地址中。一般 页 既指一组线性地址,又指包含这组地址中的数据。分页单元把所有的 ram 分成固定长度的页框 (page frame),也成物理页。每一页框包含一个页 (page),也就是说一个页框的长度与一个页的长度一致。
页框是主存的一部分,因此也是一个存储区域。区分一页和一个页框是很重要的,前者只是一个数据块,可以存放任何页框或者磁盘中。把线性地址映射到物理地址的数据结构称为页表 (page table)。页表存放在主存中,并在启用分页单元之前必须有内核对页表进行适当的初始化。 x86_64 的 linux 内核采用 4 级分页模型,一般一页 4k,4 种页表:
a、页全局目录
b、页上级目录
c、页中间目录
d、页表
页全局目录包含若干页上级目录,页上级目录又依次包含若干页中间目录的地址,而页中间目录又包含若干页表的地址。每个页表项指向一个页框。线性地址被分成 5 部分。
3)numa 架构
随着 cpu 进入多核时代,多核 cpu 通过一条数据总线访问内存延迟很大,因此 numa 架构应运而生,numa 架构全称为非一致性内存架构 (non uniform memory architecture),系统的物理内存被划分为几个节点 (node),每个 node 绑定不同的 cpu 核,本地 cpu 核直接访问本地内存 node 节点延迟最小。
可以通过 lscpu 命令查看 numa 与 cpu 核的关系。
$ lscpuarchitecture: x86_64cpu op-mode(s): 32-bit, 64-bitbyte order: little endiancpu(s): 32on-line cpu(s) list: 0-31thread(s) per core: 2core(s) per socket: 8socket(s): 2numa node(s): 2vendor id: genuineintelcpu family: 6model: 62stepping: 4cpu mhz: 2001.000bogomips: 3999.43virtualization: vt-xl1d cache: 32kl1i cache: 32kl2 cache: 256kl3 cache: 20480knuma node0 cpu(s): 0-7,16-23 #这些核绑定在numa 0numa node1 cpu(s): 8-15,24-31 #这些核绑定在numa 1  
4)伙伴关系算法
linux 内核通过著名伙伴关系算法为分配一组连续的页框而建立一种健壮、稳定的内存分配策略,是内核中一种内存分配器,并解决了内存管理外碎片的问题,外碎片是指频繁地请求和释放不同大小的一组连续页框,必然导致在已分配的页框的块分散了许多小块的空闲页框。
5)slab 机制
slab 机制的核心思想是以对象的观点来管理内存,主要是为了解决内部碎片,内部碎片是由于采用固定大小的内存分区,即以固定的大小块为单位来分配,采用这种方法,进程所分配的内存可能会比所需要的大,这多余的部分便是内部碎片。slab 也是内核中一种内存分配器,slab 分配器基于对象进行管理的,所谓的对象就是内核中的数据结构(例如:task_struct,file_struct 等)。
相同类型的对象归为一类,每当要申请这样一个对象时,slab 分配器就从一个 slab 列表中分配一个这样大小的单元出去,而当要释放时,将其重新保存在该列表中,而不是直接返回给伙伴系统,从而避免内部碎片。上面中说到的 dentry 对象就是通过 slab 分配器分配的一种对象。
slab 和伙伴系统是上下级的调用关系,伙伴关系按照页管理内存,slab 按照字节管理,slab 先从伙伴系统获取数个页的内存,然后切成分成固定的小块(称为 object),然后再按照声明的对象数据结构分配对象。
6)进程内存分布
所有进程都必须占用一定数量的内存,这些内存用来存放从磁盘载入的程序代码,或存放来自用户输入的数据等。内存可以提前静态分配和统一回收,也可以按需动态分配和回收。对于普通进程对应的内存空间包含 5 种不同的数据区:
a、代码段 (text):程序代码在内存中的映射,存放函数体的二进制代码,通常用于存放程序执行代码 (即 cpu 执行的机器指令)。
b、数据段 (data):存放程序中已初始化且初值不为 0 的全局变量和静态局部变量。数据段属于静态内存分配 (静态存储区),可读可写。
c、bss 段 (bss):未初始化的全局变量和静态局部变量。
d、堆 (heap):动态分配的内存段,大小不固定,可动态扩张 (malloc 等函数分配内存),或动态缩减 (free 等函数释放)。
e、栈 (stack):存放临时创建的局部变量。
linux 内核是操作系统中优先级最高的,内核函数申请内存必须及时分配适当的内存,用户态进程申请内存被认为是不紧迫的,内核尽量推迟给用户态的进程动态分配内存。
a、请求调页,推迟到进程要访问的页不在 ram 中时为止,引发一个缺页异常。
b、写时复制 (cow),父、子进程共享页框而不是复制页框,但是共享页框不能被修改,只有当父 / 子进程试图改写共享页框时,内核才将共享页框复制一个新的页框并标记为可写。
7)linux 内存检测工具
a、free 命令可以监控系统内存
$ free -h total used free shared buff/cache availablemem: 31gi 13gi 8.0gi 747mi 10gi 16giswap: 2.0gi 321mi 1.7gi  
b、top 命令查看系统内存以及进程内存
• virt virtual memory size (kib):进程使用的所有虚拟内存,包括代码(code)、数据(data)、共享库(shared libraries),以及被换出(swap out)到交换区和映射了(map)但尚未使用(未载入实体内存)的部分。
• res resident memory size (kib):进程所占用的所有实体内存(physical memory),不包括被换出到交换区的部分。
• shr shared memory size (kib):进程可读的全部共享内存,并非所有部分都包含在 res 中。它反映了可能被其他进程共享的内存部分。
c、smaps 文件
cat /proc/$pid/smaps 查看某进程虚拟内存空间的分布情况
0082f000-00852000 rw-p 0022f000 08:05 4326085 /usr/bin/nginx/sbin/nginxsize: 140 kbrss: 140 kbpss: 78 kbshared_clean: 56 kbshared_dirty: 68 kbprivate_clean: 4 kbprivate_dirty: 12 kbreferenced: 120 kbanonymous: 80 kbanonhugepages: 0 kbswap: 0 kbkernelpagesize: 4 kbmmupagesize: 4 kbd、vmstat vmstat 是 virtual meomory statistics(虚拟内存统计)的缩写,可实时动态监视操作系统的虚拟内存、进程、cpu 活动。## 每秒统计3次$ vmstat13procs -----------memory---------------- ---swap-- -----io---- --system-- -----cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 000233483840758304207955960001000010000 000233483936758304207955960000105215690010000 00023348392075830420795596000096615580010000  
e、meminfo 文件
linux 系统中 /proc/meminfo 这个文件用来记录了系统内存使用的详细情况。
$ cat /proc/meminfomemtotal: 8052444 kbmemfree: 2754588 kbmemavailable: 3934252 kbbuffers: 137128 kbcached: 1948128 kbswapcached: 0 kbactive: 3650920 kbinactive: 1343420 kbactive(anon): 2913304 kbinactive(anon): 727808 kbactive(file): 737616 kbinactive(file): 615612 kbunevictable: 196 kbmlocked: 196 kbswaptotal: 8265724 kbswapfree: 8265724 kbdirty: 104 kbwriteback: 0 kbanonpages: 2909332 kbmapped: 815524 kbshmem: 732032 kbslab: 153096 kbsreclaimable: 99684 kbsunreclaim: 53412 kbkernelstack: 14288 kbpagetables: 62192 kbnfs_unstable: 0 kbbounce: 0 kbwritebacktmp: 0 kbcommitlimit: 12291944 kbcommitted_as: 11398920 kbvmalloctotal: 34359738367 kbvmallocused: 0 kbvmallocchunk: 0 kbhardwarecorrupted: 0 kbanonhugepages: 1380352 kbcmatotal: 0 kbcmafree: 0 kbhugepages_total: 0hugepages_free: 0hugepages_rsvd: 0hugepages_surp: 0hugepagesize: 2048 kbdirectmap4k: 201472 kbdirectmap2m: 5967872 kbdirectmap1g: 3145728 kb


使用Infinity和InfinityXT探针提高生产率
分立式半桥栅极驱动器设计
正方科技举办无人机锂电池发布会
采用GPU加速全新编码功能加速改进视频编辑工作流
超高屏占比对飙小米mix,联想ZUKedge开箱图集
Linux内存管理暴涨的问题学习
2020年十大机器学习研究进展
继苹果关机门事件后苹果爆炸门事件又起,这次苹果会召回“问题机”吗?
VGA接口/DVI接口/HDMI接口/DP接口的基础知识
三星的物联网之路
长鑫存储“存储器的检测方法及存储器”专利公布
高熵层状氧化物HEO5中快速容量衰减的机制研究
细菌检测仪器是什么,它有哪些特点
壁挂数码管王字壳温湿度变送器(485型)
电阻分压网络电路设计分析
LSOC-14.5-C倾角传感器安装前需要注意什么
高速USB转接芯片沁恒CH347介绍
无热电动势的补偿电缆和插头连接器的短路因素
LED电动摩托车灯驱动方案12-80V 9V/3A AP2400 DC-DC降压恒流IC
汽车电子“供热”问题是怎样的