嵌入式系统中的内存分配

时间:2018-12-25 13:30:08

标签: c memory memory-management embedded malloc

Linux / Unix系统通常在x86 CPU体系结构上运行,该体系结构提供了用于内存映射的MMU,但是我猜嵌入式系统缺少此功能,因此没有mallocfree

我看到ES程序员可以创建一个大的静态缓冲区来保留内存:

unsigned char mem[10240];

但是我不知道它是如何工作的,缓冲区在哪里精确启动,以及C编译器如何将变量定义映射到某些平台上的实际内存位置。

我还在ES标头中看到了这样的常量(从ARM STM32L1xx中提取):

#define SRAM_BASE ((uint32_t)0x20000000) /*!< SRAM base address in the alias region */

我了解SRAM代表“静态” RAM,但“基地址”表示保留供堆/栈访问的存储区的开始,因此程序员可以使用指向该基地址的指针来分配和释放RAM块和块的链接列表?

2 个答案:

答案 0 :(得分:3)

我的印象是,与此问题有关的一些误解。让我尝试对它们进行整理。

函数malloc和free不需要MMU。
您已经看到使用MMU的一种实现方式,但这并不意味着总是需要它。
虽然MMU可能使实现更容易,并且在PMMU的情况下甚至可以推迟尝试的malloc的失败(通过使用虚拟内存),但可以在静态分配的变量上实现这两个功能。使用适当的大型阵列(如您所述)是一种方法。我至少在两种情况下这样做,以适应使用动态内存分配的应用程序的实现。
请注意,在嵌入式系统上,需要扩展有关系统行为的知识,以便使用适当的算法和大小来避免内存不足(通常是由于碎片化而引起或耗尽)。但是对于嵌入式系统,有时知识是可用的。

使用这样的数组意味着,像其他任何(大)变量一样,链接器通常会首先对其进行完美分配。
因此,地址由链接器根据配置和系统属性确定。一个可能的位置是BSS段内(没有特定值初始化的全局或静态变量)。

问题的最后一段特别不清楚,似乎在多个主题之间切换。
是的,SRAM用于静态RAM。但是,“静态”与根据C语法的任何含义都不相关。 SRAM的“静态”是指存储器的硬件​​实现。与动态RAM相反。动态RAM比静态RAM便宜得多,但是它需要定期刷新。实际上,刷新通常是由MMU处理的,但是我看不到您提到的MMU需要的其他情况。 SRAM可以仅通过电源来保持其内容,而DRAM在相当短的时间内丢失内容。因此,SRAM可用于嵌入式系统中,以保持CPU实际上处于死机状态的各个阶段的内容,通常称为“低功耗模式”。

术语“基址”具有许多含义。它可以参考例如用于分配BSS变量的区域的最低地址,或用于动态内存管理的区域的最低地址。

progorammer不是通过提供指针来动态分配内存,而是通过请求指针来完成,这是例如e的返回值。成功的malloc尝试。

该实现将动态内存分配在专门用于该目的的内存区域内(可能是提到的大型数组)。如果有可用的内存(例如,在空闲块的链接列表中仍找到块),它将返回其中之一的地址(当然,其后是足够的可用空间以容纳请求的大小),并保留剩余信息(如果有)空闲内存(例如,通过将现在较小的空间重新插入到链表中)。如果没有足够的内存,则返回值表示失败。

答案 1 :(得分:2)

嵌入式系统是一个非常宽泛的术语。这可能意味着没有操作系统的微控制器或带有操作系统的“普通”计算机。

一些例子:

  • uC读取传感器并控制通过管道的水流。
  • Raspberry Pi在Linux上控制无人机(已嵌入无人机)中运行
  • 控制激光切割机的PC计算机。
  • 射电望远镜系统中的大型计算机

因此,您的问题没有答案。

uC的编程方式与无人机所使用的RPi完全不同。

但是您的问题可能与我认为的uC系统有关。

通常,uC系统编程有时被称为“裸机”,因为程序员在其应用程序与实际硬件之间没有直接的软件(抽象层)。因此,它们直接访问内存,硬件寄存器和其他资源。甚至在裸机开发中使用的操作系统(称为RTOS-es)甚至与Linux或Windows之类的常规操作系统也不相似。它们是与裸机应用程序链接在一起的更多库,仅提供任务管理和通信,同步和数据交换的机制。

您在评论中提出的一些问题

DMA-直接内存访问-允许在不使用处理器内核的情况下在内存与外围设备或内存之间进行数据传输。某些uC具有非常复杂的DMA和事件系统-例如,定时器溢出会触发ADC,ADC会触发DMA传输,将转换后的数据存储到内存中。

链接器脚本-定义在内存中存储的内容,方式和位置。它们可能使我非常复杂,并指示要包含或排除的代码或数据以及如何组织程序存储器。下面是STM32系列uC的链接脚本示例

/* Entry Point */
ENTRY(Reset_Handler)

_estack = 0x10004000;    /* end of RAM */

/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x100;      /* required amount of heap  */
_Min_Stack_Size = 0x1000; /* required amount of stack */

/* Specify the memory areas */
MEMORY
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 64K
CCMRAM (rw)      : ORIGIN = 0x10000000, LENGTH = 16K
FLASH (rx)      : ORIGIN = 0x8000000 + 32K, LENGTH = 512K - 34K
}

_FLASH_SIZE = LENGTH(FLASH);

/* Define output sections */
SECTIONS
{
  /* The startup code goes first into FLASH */
  .isr_vector :
  {
    . = ALIGN(4);
    _vectors_start = .;
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
    _vectors_end = .;
  } >FLASH

  .sizedata :
  {
    . = ALIGN(4);
    KEEP(*(.sizedata))
    KEEP(*(.sizedata*)) 
    . = ALIGN(4);
  } >FLASH  

    .flashdata :
  {
    . = ALIGN(4);
    KEEP(*(.rodata))         /* .rodata sections (constants, strings, etc.) */
    KEEP(*(.rodata*))       /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >FLASH

  /* The program code and other data goes into FLASH */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >FLASH

  /* Constant data goes into FLASH */
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >FLASH




  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
  .ARM : {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
  } >FLASH

  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  } >FLASH
  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
  } >FLASH
  .fini_array :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
  } >FLASH


  /* used by the startup to initialize data */
  _sidata = LOADADDR(.data);
   _ROMEND = .;


  /* Initialized data sections goes into CCMRAM, load LMA copy after code */
  .data : 
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)           /* .data sections */
    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */
  } >CCMRAM AT> FLASH

   _ROMSIZE = _ROMEND - ORIGIN(FLASH) + _edata - _sdata;

  _siccmram = ORIGIN(CCMRAM);
    _sconfig = _ROMSIZE;
  _econfig = _sconfig + 2K;
    .bss :
  {
    /* This is used by the startup in order to initialize the .bss secion */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)

    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >CCMRAM

  /* RAM section 
  */

      /* Uninitialized data section */
   .dummy :
   {
   . = ALIGN(4);
   *(.dummy)
   *(.dummy*)
   . = ALIGN(4);
   } > RAM AT > FLASH

  .ram :
  {  
    . = ALIGN(4);
    _sram = .;       /* create a global symbol at ccmram start */
    *(.ram)
    *(.ram*)   
    . = ALIGN(4);
    _eram = .;       /* create a global symbol at ccmram end */
  } >RAM 

  /* User_heap_stack section, used to check that there is enough RAM left */
  ._user_heap_stack (NOLOAD):
  {
    . = ALIGN(8);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(8);
  } >CCMRAM



  /* Remove information from the standard libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }


  .ARM.attributes 0 : { *(.ARM.attributes) }
}

MMU-有些uC具有内存管理单元-但通常此外围设备仅保护某些内存区域。由于没有操作系统,因此也没有文件系统-因此不存在mmap之类的东西。

  #define SRAM_BASE ((uint32_t)0x20000000)

它仅以人类可读的形式定义某物的地址。在这种情况下,它可能只是SRAM存储器开始的地址。另一个例子:

#define PERIPH_BASE           ((uint32_t)0x40000000U)
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x00010000U)
#define SPI1_BASE             (APB2PERIPH_BASE + 0x00003000U)
#define SPI1                ((SPI_TypeDef *) SPI1_BASE)