当前位置:   article > 正文

嵌入式内存管理及调试方法

嵌入式内存管理及调试方法

概述

linux内存的使用情况将直接导致系统运行的稳定性,因此,如何查看内存使用情况,以及了解内核内存分配情况是十分必要的。

内存分类

并不是buffer+caches就是系统可用内存,要看情况,有些cache无法被回收,具体看下面章节

实际可用内存是:buffer + caches - 无法被释放的caches空间 = 可用内存

free

未分配的内存。内核不需要额外的操作,就可以使用它。

buffers

写缓存
缓冲区。用于在读写速度不同步的设备之间使用。

多用于快速设备向慢速设备写数据时。

例如:当快速设备(例如内存)的数据写入读取慢速设备(例如磁盘)中时,快速设备的数据将先被拷贝到buffers缓冲区中,当buffers中数据到达一定量时,慢速设备才去buffers中读取这些数据。这样,快速设备就可以有时间去做别的事情了。

Cached

读缓存
缓存区。用于缓存从慢速设备读到的数据,方便下次再次使用这些数据时,速度能快一些。
例如:程序open、read磁盘上的文件,程序把文件的内容拷贝到自己buf中,同时,这些文件数据还被存在了cached空间中。

cached回收

Linux内核会在内存将要耗尽的时候,触发内存回收的工作,以便释放出内存给急需内存的进程使用。一般情况下,这个操作中主要的内存释放都来自于对buffer/cache的释放。尤其是被使用更多的cache空间。既然它主要用来做缓存,只是在内存够用的时候加快进程对文件的读写速度,那么在内存压力较大的情况下,当然有必要清空释放cache,作为free空间分给相关进程使用。所以一般情况下,我们认为buffer/cache空间可以被释放,这个理解是正确的。

但是这种清缓存的工作也并不是没有成本。理解cache是干什么的就可以明白清缓存必须保证cache中的数据跟对应文件中的数据一致,才能对cache进行释放。所以伴随着cache清除的行为的,一般都是系统IO飙高。因为内核要对比cache中的数据和对应硬盘文件上的数据是否一致,如果不一致需要写回,之后才能回收。

在系统中除了内存将被耗尽的时候可以清缓存以外,我们还可以使用下面这个文件来人工触发缓存清除的操作:

echo 1 > /proc/sys/vm/drop_caches
  • 1

当然,这个文件可以设置的值分别为1、2、3。它们所表示的含义为:
echo 1 > /proc/sys/vm/drop_caches:表示清除pagecache。

echo 2 > /proc/sys/vm/drop_caches:表示清除回收slab分配器中的对象(包括目录项缓存和inode缓存)。slab分配器是内核中管理内存的一种机制,其中很多缓存数据实现都是用的pagecache。

echo 3 > /proc/sys/vm/drop_caches:表示清除pagecache和slab分配器中的缓存对象。

注意

触发OOM时内核会回收cached,但程序正常执行完毕退出后,cached不会被自动回收。

cached无法被回收的情况

1、tmpfs中的文件
存放在tempfs中的文件,将占用page caches,该占用无法手动或(系统)自动释放掉
2、共享内存
申请的共享内存将在caches中永久占用,即使退出调用的进程,caches也无法被回收,除非调用shmctl关闭共享内存才能被回收

OOM

即:Out of memory: Kill process
当系统可用内存不足时(free+buffer+cache),kernel的out_of_memory()将被触发,它将检查当前最占用内存的进程,并将其杀掉

触发OOM导致驱动异常

示例:上层OOM导致网卡驱动kmalloc失败

OOM触发重启

触发OOM表示系统内存吃紧,如果不杀掉占用内存较多的进程将导致内核出现异常。

理论上杀掉个别进程就可以了,为什么实际杀掉了很多进程,还杀掉了watchdog?

如何避免?

提高OOM触发条件
默认触发条件位于:cat /proc/sys/vm/min_free_kbytes
单位为:kb
根据实际调整该值

在启动脚本中增加:
echo 2048 > /proc/sys/vm/min_free_kbytes
  • 1
  • 2

内存异常调试方法

valgrind

常用命令

valgrind --leak-check=full --tool=memcheck --log-file=memcheck.log -v ./your_program
结果将输出到memcheck.log文件中
-v: 显示更多细节

log分析

多关注"definitely lost"信息:它表示有内存被分配了,但是没有被释放,而且程序也无法再次访问到这些内存。这是一个严重的内存泄露。

执行上述命令,目标进程会随之运行,如果发现valgrind执行进程和单独执行进程状态上有差异,可能是进程调用了某个库的接口导致,如果不影响运行,可将该接口屏蔽掉,然后再用valgrind调试。

valgrind会在出现某些重大错误时,直接退出程序,而直接执行该程序时却正常,此时需要判断该错误是否是误报,是需要改代码跳过该部分,还是解决该问题。

使用如下命令,长跑后ctrl+c,输出信息更多
valgrind --show-reachable=yes --leak-check=full --track-origins=yes --trace-children=yes ./gateway

分析:
LEAK SUMMARY : 可看到整体内存泄漏的情况
HEAP SUMMARY : 下面将列出具体内存泄漏的位置
indirectly lost : 确认丢失。程序中存在内存泄露,应尽快修复。当程序结束时如果一块动态分配的内存没有被释放且通过程序内的指针变量均无法访问这块内存则会报这个错误。
possibly lost : 可放到最后解决,当indirectly lost全部解决完毕,此时运行程序时,cat /proc/pid/status 中虚拟内存还在不断增长,就需要解决possibly lost
Mismatched free() / delete / delete []
释放内存的接口使用错误,例如malloc申请的内存,使用delete释放将出现改提示

注意:valgrind不支持armv5指令集

Asan

g++4.8 以上的版本自带了 ASAN 工具,只要编译时指定好参数,编译完成后正常启动运行程序就可以了使用
编译时增加选项
Makefile文件

CFLAGS += -g -O0 -fsanitize=address -fno-omit-frame-pointer
  • 1

-O0 ,编译时不让编译器做优化。
Cmake文件

option(BUILD_ASAN_OPTION "use Asan build option, default off" OFF)

if(BUILD_ASAN_OPTION) 
message(STATUS "tinyhttp OPEN_ASAN_OPTION") # 置C++ 编译选项,也可以通过指令ADD_DEFINITIONS 
SET( CMAKE_CXX_FLAGS_ASAN "-O0 -g -fsanitize=address -fno-omit-frame-pointer" 
    CACHE STRING 
    "Flags used by the C++ compiler during asan builds."
    FORCE )
# C编译权限 
SET( CMAKE_C_FLAGS_ASAN "-O0 -g -fsanitize=address -fno-omit-frame-pointer" 
    CACHE STRING 
    "Flags used by the C compiler during asan builds." 
     FORCE )
# 共享库 
SET( CMAKE_SHARED_LINKER_FLAGS_ASAN "-static-libasan" 
    CACHE STRING
    "Flags used for linking shared libraries during asan builds."
    FORCE ) 
endif()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

编译完毕直接执行即可。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/275284
推荐阅读
相关标签
  

闽ICP备14008679号