简体   繁体   English

嵌入式系统中的内存分配

[英]Memory allocation in embedded systems

Linux/Unix systems often run on x86 CPU architecture which provides a MMU for memory mapping, but I guess embedded systems lack this, so no malloc and free . Linux / Unix系统通常在x86 CPU体系结构上运行,该体系结构提供了用于内存映射的MMU,但是我猜嵌入式系统缺少此功能,因此没有mallocfree

I saw that ES programmers can create a big static buffer to reserve memory: 我看到ES程序员可以创建一个大的静态缓冲区来保留内存:

unsigned char mem[10240];

But I wonder how it works, where does the buffer start exactly, and how the C compiler can map variable definitions to actual memory locations on some platforms. 但是我不知道它是如何工作的,缓冲区是从哪里准确开始的,以及C编译器如何将变量定义映射到某些平台上的实际内存位置。

I have also seen such constants in ES headers (extracted from the ARM STM32L1xx ): 我还在ES标头中看到了这些常量(从ARM STM32L1xx提取):

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

I understand that SRAM stands for "Static" RAM but does "base address" means the beginning of the memory area reserved to heap/stack access, so a programmer could allocate and free chunks of RAM using a pointer to this base address and a linked list of blocks ? 我知道SRAM代表“静态” RAM,但“基地址”的意思是保留用于堆/栈访问的内存区域的开始,因此程序员可以使用指向该基地址的指针和链接的RAM来分配和释放RAM块块列表?

I get the impression that there are some misunderstandings related to this question. 我的印象是,与此问题有关的一些误解。 Let me try to sort them out. 让我尝试整理一下。

The function malloc and free do not require an MMU. 函数malloc和free不需要MMU。
The fact that you have seen one implementation using the MMU does not mean it is always needed. 您已经看到使用MMU的一种实现方式,但这并不意味着始终需要它。
While an MMU might make implementing easier and in case of a PMMU even postpone the failure of attempted mallocs (by using virtual memory), implementing those two functions on statically allocated variables is possible. 虽然MMU可能使实现更容易,并且在PMMU的情况下甚至可以推迟尝试的malloc的失败(通过使用虚拟内存),但可以在静态分配的变量上实现这两个功能。 Using appropriatly large arrays (as you mention) is a way to do so. 使用适当的大型阵列(如您所述)是一种方法。 I did so in at least two different cases, to accommodate immplementations of applications which use dynamic memory allocation. 我至少在两种不同的情况下这样做,以适应使用动态内存分配的应用程序的实现。
Note that on embedded systems extended knowledge about the system behaviour is needed, in order to use appropriate algorithms and size to avoid running out of memory (usually caused or exarcerbated by fragementation). 请注意,在嵌入式系统上,需要扩展有关系统行为的知识,以便使用适当的算法和大小来避免内存不足(通常是由于碎片化而引起或耗尽)。 But for embedded system that knowledge is sometimes available. 但是对于嵌入式系统,有时知识是可用的。

Using such an array means that it is first allocated perfectly normally by the linker, like any other (large) variable. 使用这样的数组意味着,链接器首先会像其他任何(大型)变量一样,对其进行正常分配。
The address is therefor determined by the linker, according to configuration and system attributes. 因此,地址由链接器根据配置和系统属性确定。 One likely location is inside the BSS segment (global or static variables without specific value initialisation). 一个可能的位置在BSS段内(没有特定值初始化的全局或静态变量)。

The last paragraph of your question is especially unclear and seems to switch between multiple topics. 问题的最后一段尤其不清楚,似乎在多个主题之间切换。
Yes, SRAM is for static RAM. 是的,SRAM用于静态RAM。 The "static" in that is however not related to any of the meanings according to C syntax. 但是,“静态”与根据C语法的任何含义都不相关。 The "static" of SRAM refers to the hardware implementation of the memory. SRAM的“静态”是指存储器的硬件​​实现。 It is in contrast to dynamic RAM. 与动态RAM相反。 Dynamic RAM is much cheaper than static RAM, it does however require regular refreshing. 动态RAM比静态RAM便宜得多,但是它需要定期刷新。 The refreshing is practically always handled by an MMU but I do not see any relation to the other case of MMU-need you mentioned. 实际上,刷新通常是由MMU处理的,但是我看不到您提到的MMU需要的其他情况。 SRAM can keep its content just with power supply, DRAM loses content after quite short a time. SRAM可以仅通过电源来保持其内容,而DRAM在相当短的时间内丢失内容。 SRAM therefor can be used in embedded systems to keep content across phases in which the CPU is practically dead, often referred to as "low power modes". 因此,SRAM可用于嵌入式系统中,以保持CPU实际上处于死机状态的各个阶段的内容,通常称为“低功耗模式”。

The term "base address" has many meanings. 术语“基址”具有许多含义。 It can refer to eg the lowest address of an area used to allocate BSS variables, or the lowest address of an area used for dynamic memory management. 它可以指的是例如用于分配BSS变量的区域的最低地址,或用于动态内存管理的区域的最低地址。

A progorammer allocates memory dynamically NOT by giving a pointer, it is done by asking for a pointer instead, which is the return value of eg a successful malloc attempt. 编程器不会通过提供指针来动态分配内存,而是通过请求指针来完成,这是例如成功进行malloc尝试的返回值。

The implementation allocates dynamic memory inside a memory area available for specifically that purpose (which might be the mentioned large array). 该实现在专门用于该目的的可用内存区域内分配动态内存(可能是提到的大型数组)。 If memory is available (eg there are still blocks found in a linked list of free blocks), it returns an address inside one of them (which is of course followed by enough free space to accomodate the requested size) and keeps information on the remaining (if any) free memory (eg by inserting that now smaller space back into the linked list). 如果有可用的内存(例如,在空闲块的链接列表中仍找到块),它将返回其中之一的地址(当然,其后是足够的可用空间以容纳请求的大小),并保留剩余信息(如果有)空闲内存(例如,通过将现在较小的空间重新插入到链表中)。 If not enough memory is left anymore, the return value indicates failure. 如果没有足够的内存,则返回值指示失败。

Embedded System is a very broad term. 嵌入式系统是一个非常宽泛的术语。 It may mean the micro controller without the operating system or "normal" computer with the OS. 这可能意味着没有操作系统的微控制器或带有操作系统的“普通”计算机。

Some examples: 一些例子:

  • uC reading the sensors and controlling the water flow through the pipe. uC读取传感器并控制通过管道的水流量。
  • Raspberry Pi run under Linux controlling the drone (it is embedded into the drone) Raspberry Pi在Linux上控制无人机(在无人机中嵌入)下运行
  • PC computer controlling the laser cutter . PC计算机控制激光切割机。
  • mainframe computer in a radio telescope system 射电望远镜系统中的大型计算机

So there is no one answer to your question. 因此,您的问题没有答案。

uCs will be programmed completely different way than the RPi used in the drone. uC的编程方式与无人机所用的RPi完全不同。

But your question is probably about the uC systems I think. 但是您的问题可能与我认为的uC系统有关。

uC systems are usually programming is sometimes called a "bare metal" because programmers do not have the immediate software (abstraction layer) between their application and the actual hardware. 通常,uC系统编程有时被称为“裸机”,因为程序员在其应用程序与实际硬件之间没有直接的软件(抽象层)。 So they access the memory, hardware registers and another resources directly. 因此,它们直接访问内存,硬件寄存器和其他资源。 Even operating systems used in the bare metal development (called RTOS-es) are not even similar to the normal OSes like Linux or Windows. 甚至在裸机开发中使用的操作系统(称为RTOS-es)甚至与Linux或Windows之类的常规操作系统也不相似。 They are more libraries linked together with the bare metal application and just provide mechanisms of task management and communication, synchronization and data exchange. 它们是与裸机应用程序链接在一起的更多库,仅提供任务管理和通信,同步和数据交换的机制。

Some questions you asked in the comments 您在评论中提出的一些问题

DMA - Direct memory access - allows data transfer between the memory and peripherals or memory with no use of the processor core. DMA-直接内存访问-允许在不使用处理器内核的情况下在内存与外围设备或内存之间进行数据传输。 Some uC have very complicated DMA & event systems - for example the timer overflow can trigger the ADC which triggers the DMA transfer storing the converted data into the memory. 某些uC具有非常复杂的DMA和事件系统-例如,定时器溢出会触发ADC,ADC触发DMA传输,将已转换的数据存储到内存中。

Linker Script - defines what, how and where is stored in the memory. 链接描述文件-定义在存储器中存储的内容,方式和位置。 They may me very complicated and instruct that code or data to include or exclude, and how to organize the program memory. 它们可能使我非常复杂,并指示要包含或排除的代码或数据以及如何组织程序存储器。 Below you have an example of the linker script for the STM32 family uC 下面是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 - some uCs have memory management units - but usually this peripheral only protects some memory areas. MMU(某些uC具有内存管理单元),但通常此外围设备仅保护某些内存区域。 As you do not have the OS, you also do not have the file system - so nothing like mmap exists. 由于没有操作系统,因此也没有文件系统-因此不存在mmap之类的东西。

  #define SRAM_BASE ((uint32_t)0x20000000)

it only defines in the human readable form the address of something. 它仅以人类可读的形式定义事物的地址。 In this case probably it is just the address of the beginning of the SRAM memory. 在这种情况下,它可能只是SRAM存储器开始的地址。 Another example: 另一个例子:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM