内存管理优化:内存泄漏检测、Swap分区优化、大页内存配置、内存碎片整理

内存管理,说实在的,是嵌入式POS机性能调优里最容易出幺蛾子的环节。我见过太多系统跑着跑着就卡死,最后发现是内存泄漏。也见过Swap配置不当,导致IO瓶颈直接把交易吞吐量腰斩。今天咱们就把这几个硬骨头啃下来。

内存泄漏检测:别让内存悄悄溜走

内存泄漏在POS机上特别隐蔽。你想想看,POS机可能连续运行几个月不重启。每天泄漏几十KB,三个月下来就是几个GB。系统不崩才怪。

我个人习惯用 Valgrind 做静态检测。但嵌入式环境资源有限,Valgrind跑起来太慢。这时候我推荐用 AddressSanitizer (ASan)

核心思路:编译时加上ASan,运行时自动检测内存泄漏。

// 编译命令示例
gcc -fsanitize=address -g -o pos_app main.c

// 运行后,泄漏信息会直接打印
./pos_app

// 输出示例
==12345==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 128 byte(s) in 1 object(s) allocated from:
    #0 0x7f... in malloc
    #1 0x4a... in create_transaction /src/transaction.c:42

嗯,这里要注意。ASan会增加内存开销,大概2-3倍。所以只建议在测试阶段开启。线上版本一定要关掉。

我的经验:曾经有个项目,POS机每处理100笔交易就泄漏4KB。用ASan一查,发现是某个回调函数里malloc了缓冲区,但忘记free。修复后系统稳定运行了半年没重启。

Swap分区优化:别让内存溢出杀死交易

嵌入式POS机的物理内存通常不大,512MB到1GB很常见。当内存不够时,Swap分区就成了救命稻草。但Swap用不好,反而会拖慢系统。

我建议遵循以下原则:

  • Swap大小:物理内存的1-2倍。512MB内存,Swap设1GB就够。
  • Swap位置:放在高速存储上。eMMC比SD卡快,优先选eMMC。
  • Swappiness值:这个参数控制内核使用Swap的倾向。默认60,对POS机来说太高了。

调优命令:

# 查看当前swappiness
cat /proc/sys/vm/swappiness

# 临时调低到10(更倾向于使用物理内存)
echo 10 > /proc/sys/vm/swappiness

# 永久生效
echo "vm.swappiness=10" >> /etc/sysctl.conf

为什么会这样?因为POS机的交易处理对延迟敏感。如果频繁触发Swap,磁盘IO会成为瓶颈。我曾经调过一台POS机,swappiness从60降到10后,交易响应时间从200ms降到了80ms。

避坑指南:我曾经把Swap分区放在一个慢速TF卡上。结果系统一跑压力测试,Swap读写导致CPU iowait飙升到90%。交易直接超时。后来换到eMMC,问题解决。

大页内存配置:减少TLB Miss,提升性能

大页内存,说白了就是让CPU的TLB(页表缓存)能覆盖更多内存。默认4KB一页,TLB只能缓存几百个条目。换成2MB的大页,覆盖范围直接扩大512倍。

POS机上的数据库或支付核心模块,特别适合用大页。

# 查看当前大页配置
cat /proc/meminfo | grep HugePages

# 配置2MB大页,分配64个(共128MB)
echo 64 > /proc/sys/vm/nr_hugepages

# 挂载hugetlbfs
mount -t hugetlbfs hugetlbfs /dev/hugepages

# 应用程序使用大页(示例代码片段)
#include <sys/mman.h>
void *ptr = mmap(NULL, 128*1024*1024, 
                 PROT_READ | PROT_WRITE,
                 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
                 -1, 0);

我的经验:在某个支付网关模块上,启用大页后,内存访问延迟降低了30%。但要注意,大页内存不能被Swap出去。所以分配时要留够余量,别把物理内存全占了。

内存碎片整理:让内存分配不再失败

嵌入式POS机运行久了,内存碎片会越来越严重。你明明还有几十MB空闲,但malloc一个16KB的缓冲区却失败了。这就是碎片化在作祟。

我常用的手段有几种:

  • 使用slab分配器:Linux内核默认用slab,对小块内存分配友好。但要注意slab本身也会产生内部碎片。
  • 定期触发内存压缩:内核提供了内存压缩机制,可以合并碎片。
  • 应用程序层面:使用内存池,避免频繁malloc/free。

内核级碎片整理命令:

# 触发内存压缩
echo 1 > /proc/sys/vm/compact_memory

# 查看碎片化程度
cat /proc/buddyinfo

# 输出示例(数值越小,碎片越少)
Node 0, zone   Normal   1024   512   256   128   64   32   16    8    4    2    1

嗯,这里有个技巧。我建议在POS机空闲时段(比如凌晨3点),用cron定时执行一次内存压缩。这样白天交易高峰期,内存分配失败的概率会大大降低。

避坑指南:我曾经在交易高峰期手动执行了compact_memory。结果内存压缩过程占用了大量CPU,导致交易延迟飙升。记住,碎片整理要在系统空闲时做。

总结一下关键点

优化项 核心手段 注意事项
内存泄漏检测 ASan、Valgrind 测试阶段开启,线上关闭
Swap分区优化 调整swappiness、选高速存储 避免频繁Swap导致IO瓶颈
大页内存配置 分配HugePages、应用层mmap 预留余量,不可被Swap
内存碎片整理 compact_memory、内存池 空闲时段执行,避免高峰期

内存管理优化,说白了就是跟系统内核斗智斗勇。你想想看,POS机每天处理几百笔交易,每一笔都涉及内存分配释放。稍有不慎,系统就会在关键时刻掉链子。把这些手段用上,至少能保证系统稳定运行个一年半载。

下一章咱们聊聊CPU调频与中断绑定,那又是另一番天地了。