打印
[综合信息]

内存管理和存储架构

[复制链接]
51|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
flycamelaaa|  楼主 | 2025-2-24 11:47 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
C语言允许程序变量在定义时就确定内存地址,通过作用域,以及关键字extern,static,实现了精细的处理机制,按照在硬件的区域不同,内存分配有三种方式:

·从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。

·在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中 ,效率很高,但是分配的内存容量有限。

·从堆上分配,亦称动态内存分配。程序在运行的时候用 malloc 或 new 申请任意多少的内存,程序员自己负责在何时用 free 或 delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但同时遇到问题也最多。



这里先看个简单的C语言实例。





//main.c

#include <stdio.h>#include <stdlib.h>

static int st_val;                   //静态全局变量 -- 静态存储区

int ex_val;                           //全局变量 -- 静态存储区

int main(void)

{

   int a = 0;                         //局部变量 -- 栈上申请

   int *ptr = NULL;                   //指针变量

   static int local_st_val = 0;       //静态变量

   local_st_val += 1;

   a = local_st_val;

   ptr = (int *)malloc(sizeof(int)); //从堆上申请空间

   if(ptr != NULL)

   {

      printf("*p value:%d", *ptr);

      free(ptr);

      ptr = NULL;

      //free后需要将ptr置空,否则会导致后续ptr的校验失效,出现野指针

   }            

}



C语言的作用域不仅描述了标识符的可访问的区域,其实也规定了变量的存储区域,在文件作用域的变量st_val和ex_val被分配到静态存储区,其中static关键字主要限定变量能否被其它文件访问,而代码块作用域中的变量a, ptr和local_st_val则要根据类型的不同,分配到不同的区域,其中a是局部变量,被分配到栈中,ptr作为指针,由malloc分配空间,因此定义在堆中,而local_st_val则被关键字限定,表示分配到静态存储区,这里就涉及到重要知识点,static在文件作用域和代码块作用域的意义是不同的:在文件作用域用于限定函数和变量的外部链接性(能否被其它文件访问), 在代码块作用域则用于将变量分配到静态存储区。

   

对于C语言,如果理解上述知识对于内存管理基本就足够。

   

但对于嵌入式C编程来说,定义一个变量,它不一定在内存,也就是SRAM中,也有可能在FLASH空间,或直接由寄存器存储(register定义变量或者高优化等级下的部分局部变量),如定义为const的全局变量定义在FLASH中,定义为register的局部变量会被优化到直接放在通用寄存器中,在优化运行速度,或者存储受限时,理解这部分知识对于代码的维护就很有意义。

   

此外,嵌入式C语言的编译器中会扩展内存管理机制,如支持分散加载机制和__attribute__((section("用户定义区域"))),允许指定变量存储在特殊的区域如(SDRAM, SQI FLASH), 这强化了对内存的管理,以适应复杂的应用环境场景和需求。





LD_ROM 0x00800000 0x10000 { ;load region size_region

    EX_ROM 0x00800000 0x10000 { ;load address = execution address

  *.o (RESET, +First)

  *(InRoot$$Sections)

  .ANY (+RO)

  }

  EX_RAM 0x20000000 0xC000 { ;rw Data

    .ANY (+RW +ZI)

  }

  EX_RAM1 0x2000C000 0x2000 {

    .ANY(MySection)

   }

  EX_RAM2 0x40000000 0x20000{

    .ANY(Sdram)

  }

}



int a[10] __attribute__((section("Mysection")));

int b[100] __attribute__((section("Sdram")));



采用这种方式,我们就可以将变量指定到需要的区域,这在某些情况下是必须的,如做GUI或者网页时因为要存储大量图片和文档,内部FLASH空间可能不足,这时就可以将变量声明到外部区域,另外内存中某些部分的数据比较重要,为了避免被其它内容覆盖,可能需要单独划分SRAM区域,避免被误修改导致致命性的错误,这些经验在实际的产品开发中是常用且重要,不过因为篇幅原因,这里只简略的提供例子,如果工作中遇到这种需求,建议详细去了解下。

   

至于堆的使用,对于嵌入式Linux来说,使用起来和标准C语言一致,注意malloc后的检查,释放后记得置空,避免"野指针“,不过对于资源受限的单片机来说,使用malloc的场景一般较少,如果需要频繁申请内存块的场景,都会构建基于静态存储区和内存块分割的一套内存管理机制,一方面效率会更高(用固定大小的块提前分割,在使用时直接查找编号处理),另一方面对于内存块的使用可控,可以有效避免内存碎片的问题,常见的如RTOS和网络LWIP都是采用这种机制,我个人习惯也采用这种方式,所以关于堆的细节不在描述,如果希望了解,可以参考<C Primer Plus>中关于存储相关的说明。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

700

主题

3322

帖子

0

粉丝