Linux嵌入式设备的内存一般都不大,可能就只有32M、64M、128M、256M。无线路由器一般以64M和128M居多。之前用的64M内存的方案,由于做海外版本,增加功能,导致了内存不足,因此总结了部分经验,提供给大家,希望大家调试过程少走弯路。
一、内存不足的现象
a. 在多个无线终端挂机时,出现系统运行缓慢,甚至OOM
b. 新建性能数据低
c. 在跑大流量转发时,出现内存不足
d. 全配置下,各种功能开启时,出现内存不足
e. 内存碎片变多
由于内存不足情况,一般需要在长时间挂机的情况下出现,复现成本高,因此需要在系统设计或转测初期关注,如果到了项目后期,那基本上会阻塞项目进度,因此掌握三板斧来定位分析内存问题很重要。
二、关注系统内存方法
在Linux下基本由应用层内存和内核占用内存,这两块一会重点分析。
1、rootfs
对于嵌入式设备,rootfs的文件系统也是需要关注的,因为它没有所谓的硬盘,只有Flash,运行时无论是内核或是应用的可执行进程,都需要加载至内存。
以上示例用的是squashfs,这个文件系统是只读的,好处在于如果某个文件不用时,会处于Flash中,并不会加载至内存。
笔者很早以前用于ramfs的rootfs,这个文件系统会全部加载至内存,现在使用这种rootfs的产品很少了,毕竟内存有限。
但这不意味着rootfs大了也没有影响,相反,通过rootfs的裁剪是个必要也是很效的方案。
现在厂商编译用lzma的居多,压缩前的文件系统在30M左右,压缩后大约为6M多,压缩比6~7,因为Linux在运行时,如etc、var、tmp等目录,需要挂载为可读可写,同时web文件也占有一定大小(即使WEB目录挂载为ro,也会在访问时加载到内存),因此对于文件系统中的每个可执行文件、每个配置文件,需要了解其具体的用途,如果用不到,则不必放到文件系统中,这个非常考验我们对开发系统的了解。
2、内核占用的内存
在编译内核时,需要审查.config文件,看里面是否有用不到的或不合理的配置。
内存页大小,配置为4K
去除不必要的文件系统支持,如jffs2、yaffs2、ubi
支除不必要的编码支持
查看网络配置中的option配置、netfilter配置、l2tp/pptp/ipsec等一些协议支持
关闭一些debug特性,如kallsyms
关闭一些用不到厂商设备支持,如usb、I2C、485
......
这部分裁剪会有直观的效果,越早去裁越好,对网络设备而言,11ac、11ax的无线驱动会占较大空间,尤其BRCM方案。
运行时内存同样重要,且需要关注
如上,我们关注kernel stack大小,以及slab占用内存的情况,提到slab又要讲一堆了。slab用于Linux的内存申请,是伙伴系统buddy的精细化管理。可以简单直接为除了内核栈及预分派的DMA内存,动态内存都要通过slab来分配,通过对比系统启动时的slab信息和运行中的slab信息,我们能对比分析出内存用在哪里了。
如上,我们以链接跟踪数为例,链接跟踪是随着网络连接数据增加而增加的,即用户访问一条TCP/UDP连接,对应着系统就要占用相应的ct结构体(我们不讨论连接的保持及超时机制)。
现有连接数为437个(active_objs),当前系统有456个ct结构体,每个结构体大小为416 B(objsize),在这里可以看到:
1、nf_conntrack为专用缓存,通过调用kmem_cache_create接口来创建,这样的好处是非常直观的看到模块占用了多少内存,如果内存消耗是线性递增的,那起个好名字会非常方便定位问题,好的实现也应该如此。
2、系统已经申请了456个ct,但当前只用了437个,体现了缓存的概念。
3、每一次slab为此结构体使用从2个页(pagesperslab),提供19(objperslab)个ct结构体
4、当前的slabs数为24(num_slabs),共占用了, 24*2 * page_size = 48*4K = 192K内存
5、假设系统配置的最大ct数为2万,则需要占用 20000/19*2*4K = 8421K,也就是8M内存,此时我们得看下内存是否足够支撑,同时一些网络内核模块也依赖于ct或维护了另一个ct,那么这个线性的过程,就必然是内存考量的重点。
未完成待续
分析应用层占用内存,是否存在泄露,关注物理内存
查看内存碎片化情况buddyinfo
关注最小内存配置min_free_kbytes
关注内存水位阈值及调节zoneinfo
触发kswap的内存回收
OOM的配置
进程oom_score_adj配置
调节应用层内存的使用:受限服务及服务恢复