当前位置:首页 > 公众号精选 > 嵌入式云IOT技术圈
[导读]前面已经将所有的硬件驱动实现,验证了硬件功能。但是每一个硬件都是单独测试的,而且并不完善。

前面已经将所有的硬件驱动实现,验证了硬件功能。但是每一个硬件都是单独测试的,而且并不完善。下一步,我们需要对各个驱动进行整合完善。在整合之前,需要做一些基础工作。其中之一就是实现内存管理什么叫内存管理呢?为什么要做内存管理?前面我们已经大概了解了程序中的变量现在我们复习一下:局部变量全局变量

局部变量在进入函数时从栈空间分配,退出函数前释放。全局变量则在整个程序运行其中一直使用。在程序编译时就已经分配了RAM空间。

那还有没有第三种变量呢?可以说没有。但是如果从生存周期上看,是有的:一个变量,在多个函数内使用,但是又不是整个程序运行期间都使用。或:一个变量,在一段时间内使用,不是整个程序运行生命周期都要用,但是用这个变量的函数会退出,然后重复进入(用static定义的局部变量相当于全局变量)

如果不使用动态内存管理,这样的变量就只能定义为全局变量。如果将这些变量定义为指针,当要使用时,通过内存管理分配,使用完后就释放,这就叫做动态分配举个实际的例子:

一个设备,有三种通信方式:串口,USB,网络,在通信过程每个通信方式需要1K RAM。经过分析,3种通信方式不会同时使用。那么,如果不使用动态内存,则需要3K变量。如果使用内存管理动态分配,则只需要1K内存就可以了。(这个只是举例,如果简单的系统,确定三种方式不同时使用,可以直接复用内存)

通信方式只是举例,其实一个系统中,并不是所有设备都一直使用,如果使用动态内存管理,RAM的峰值用量将会大大减少。

内存管理方案

不发明车轮,只优化轮胎。

内存管理是编程界的一个大话题,有很多经典的方案。很多人也在尝试写新的方案。内存分配模块我们使用K&R C examples作为基础,然后进行优化。K&R是谁?就是写《C程序设计语言》的两个家伙。如果你没有这本书,真遗憾。这本书的8.7章节,<实例--存储分配程序>,介绍了一种基本的存储分配方法。代码见alloc.c,整个代码只有120行,而且结构很美

K&R 内存管理方案分析

下面我们结合代码分析这种内存分配方案。代码在wujique\Utilities\alloc文件夹。

内存分析

  • 初始化

在malloc函数中,如果是第一次调用就会初始化内存链表。代码原来是通过获取堆地址,在堆上建立内存池。我们把他改为更直观的数组定义方式。内存建立后的内存视图如下:

内存分配的最小单元是:

typedef struct ALLOC_HDR
{
 struct
{
   struct ALLOC_HDR *ptr;
   unsigned int size;/*本块内存容量*/
} s;
 unsigned int align;
 unsigned int pad;
} ALLOC_HDR;

这也就是内存管理结构体。在32位ARM系统上,这个结构体是16字节。

  • 第一次分配

每次分配,就是在一块可以分配的空间尾部切割一块出来,切割的大小是16字节的倍数,而且会比需要的内存多一块头。这块头在内存释放时需要使用。这一块,也就是内存管理的开销。

  • 分配释放后

经过多次分配释放后,内存可能如下图,绿色是两块不连续的空闲块,黄色是分配出去的块。分配出去的块,已经不在内存链表里面。

缺点

一般情况上面的代码已经能满足需求。但是,有以下缺陷:

  • 缺点1:容易碎片化

分配使用首次适应法,也即是找到一块大于等于要分配内存的空闲块,立刻进行分配。这种方法的优点是速度较快,缺点是容易内存碎片化,分配时将很多大块内存切割成小内存了。经过多次分配后,很可能出现以下情况:

空闲内存总量还有10K,但是却被分散在10个块内,而且没有大容量的内存块,再申请2K内存就出现失败。如果对时间并不是那么敏感,我们可以使用最适合法,也即是遍历空闲链表,查找一个最合适的内存(大于要分配内存且容量最小的空闲块),减少大内存被切碎的概率。需要注意的是,最适合法,除了会增加分配时间,不会减少内存碎片数量,只是增加了空闲内存的集中度假设经过多次分配后,空闲总量还是10K,也是分散在10个空闲块,但是在这10个空闲块中,会有5K的大块,再申请2K的时候,就可以申请到2K内存了。

  • 缺点2:内存消耗

内存分配方案使用了一个结构体,每次分配的最小单位就是这个结构体的大小16字节。

typedef struct ALLOC_HDR
{
 struct
{
   struct ALLOC_HDR *ptr;
   unsigned int size;/*本块内存容量*/
} s;
 unsigned int align;
 unsigned int pad;
} ALLOC_HDR;

一次分配,最少就是2个结构体(一个结构体用于管理分配出去的内存,其余结构体做为申请内存),也就是32字节。如果代码有大量小内存申请,例如申请100次8个字节

需求内存:100X8=800字节实际消耗内存100X32 = 3200字节利用率只有800/3200 =25%

如果内存分配只有25%的使用率,对于小内存嵌入式设备来说,是致命的方案缺陷。

如何解决呢?我们可以参考LINUX内存分配方案SLAB。在LINUX中,有很多模块需要申请固定大小的内存(例如node结构体),为了加快分配速度,系统会使用malloc先从大内存池中申请一批node结构体大小的内存,作为一个slab内存池。当需要分配node结构体时,就直接从slab内存池申请。同理,可以将内存分配优化为:需要小内存时,从大块内存池分配一块大内存,例如512,使用新算法管理,用于小内存分配。当512消耗尽,再从大内存池申请第二块512字节大内存。当小内存释放时,判断小块内存池是否为空,如为空,将小块内存池释放回大内存池。那如何管理这个小内存池呢?

  • 缺点3:没有管理已分配内存

内存分配没有将已分配内存管理起来。我们可以对已分配内存进行统一管理:

1 已分配内存在头部有原来的结构体,通过ptr指针,将所有已分配内存连接在已分配链表上。2 利用不使用的align跟pad成员,记录分配时间跟分配对象(记录哪个驱动申请的内存)

通过上面优化后,就可以统计已经分配了多少内存,还有多少空闲内存,哪个模块申请了最多内存等数据。

使用

1 将代码中的所有free改为为wjq_free,malloc改为wjq_malloc。

串口缓冲用了free跟malloc.fatfs的syscall.c 用了lwip的mem.h用了。

2 修改启动代码, 栈跟堆改小。不用库的malloc,堆可以完全不要。栈,还是要保留,但是不需要那么大,如果函数内用到比较大的局部变量,改为动态申请。

Stack_Size      EQU     0x00002000

AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

;Heap Configuration
;Heap Size (in Bytes);Heap_Size       EQU     0x00000010

       AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

3 内存池开了80K,编译不过

linking...
.\Objects\wujique.axf: Error: L6406E: No space in execution regions with .ANY selector matching dev_touchscreen.o(.bss).
.\Objects\wujique.axf: Error: L6406E: No space in execution regions with .ANY selector matching mcu_uart.o(.bss).
.\Objects\wujique.axf: Error: L6406E: No space in execution regions with .ANY selector matching etharp.o(.bss).
.\Objects\wujique.axf: Error: L6406E: No space in execution regions with .ANY selector matching mcu_can.o(.bss).
.\Objects\wujique.axf: Error: L6406E: No space in execution regions with .ANY selector matching netconf.o(.bss).

先把内存池改小,编译通过之后,分析 map文件,用了较多全局变量的统统改小或者改为动态申请。分析map文件,还可以检查还有没有使用库里面的malloc。

Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name
   124         32          0          4      40976       1658   alloc.o
    16          0          0          0          0       2474   def.o
    96         34       8640          4          0       1377   dev_dacsound.o
   300         36          0          0          0       2751   dev_esp8266.o
   204         38          0          1          0       1446   dev_key.o
   436         98          0         10         16       3648   dev_touchkey.o
   310         18          0         14       3000       3444   dev_touchscreen.o
   932         18          0          4          0      15981   dhcp.o
     0          0          0          0       3964       5933   dual_func_demo.o
   280         14         12          0        200       5963   etharp.o
     0          0          0          0          0      35864   ethernetif.o
     0          0          0          0          0       3820   inet.o
    98          0          0          0          0       2022   inet_chksum.o
     0          0          0          0          0       4163   init.o
   168          4          0         20          0       4763   ip.o
     0          0          4          0          0       6463   ip_addr.o
   386          4          0          0          0       4118   ip_frag.o
   264         38          0          8         16     383399   main.o
    84          8          0          0          0       1410   mcu_adc.o
    60         32          0          1         68       1511   mcu_can.o
    12          0          0          0          0        521   mcu_dac.o
   128         14          0          0          0       2352   mcu_i2c.o
    28          8          0          1          0        630   mcu_i2s.o
   336         92          0          0          0       2689   mcu_rtc.o
   430         86          0          1          0       4396   mcu_timer.o
  1564         82          0          0        328       9072   mcu_uart.o
   504         20          0         12          0       4510   mem.o
    56         10          0          0       9463       3250   memp.o
   120         14          0          0          0       1651   misc.o
     0          0          0          0         56       1066   netconf.o
   118          0          0          0          0       4267   netif.o
   684          0          0          0          0       6971   pbuf.o
    36          8        392          0       8192        824   startup_stm32f40_41xxx.o

alloc.o 内存池dev_touchscreen.o 触摸屏缓冲dual_func_demo.o USB,应该能优化memp.o 什么鬼?又一个内存池?应该是要优化掉startup_stm32f40_41xxx.o 启动代码,是栈跟堆用的RAM.

由于编译器的优化,项目没用到的代码没有编译进来,上面的map数据并不完整。等后面我们做完全部测试程序,所有用到的代码都会参与连接,到时还需要优化一次。

总结

内存管理暂时到此,等后面所有功能都完成后,再进行一次优化。如果对内存分配时间有更高要求,可使用伙伴内存分配法。大家可以参考《都江堰操作系统与嵌入式系统设计》,这个文档里面的一些软件设计策略非常好。http://www.djyos.com/?page_id=50

免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭