您的位置 首页 观点

内存! 内存! 内存! 嵌入式裸机编程最重要的事

在嵌入式裸机编程中,作为一名初级的CODER。经常要与CPU、内存等打交道。CPU作为系统的动力源,其重要程度不言而喻。但是,在裸机编程中,对内存的管理也不容忽视。如果稍微不注意,轻则,可能造成内

  在嵌入式裸机编程中,作为一名初级的CODER。常常要与CPU、内存等打交道。CPU作为体系的动力源,其重要程度显而易见。可是,在裸机编程中,对内存的办理也不容忽视。假如略微不留意,轻则,或许形成内存走漏,重则形成内存拜访反常。导致体系死机。

  嵌入式产品,对安稳性要求及其严厉。动不动就死机,那可就费事大了。以下,是我自己对嵌入式体系裸机编程的内存办理的一些简介。

  1. 万万不可运用体系自带的malloc和free。

  malloc和free在PC编程中是很好用的一种内存分配手法。可是,其在嵌入式中,就未必好用了。因为嵌入式裸机编程中,无MMU,即内存办理单元。无法完成对内存进行动态映射(不明白什么叫动态映射的同学,能够参阅网上的材料)。也便是说,实际上,malloc和free并不能完成动态的内存的办理。这需要在发动阶段专门给其分配一段闲暇的内存区域作为malloc的内存区。如STM32中的发动文件startup_stm32f10x_md.s中见以下信息:

  [plain] view plain copy

  Heap_Size EQU 0x00000800 AREA HEAP, NOINIT, READWRITE, ALIGN=3

  __heap_base

  Heap_Mem SPACE Heap_Size

  __heap_limit

  其间,Heap_Size即界说一个宏界说。数值为0x00000800。Heap_Mem则为请求一块接连的内存,巨细为 Heap_Size。简化为C言语版别如下:

  #define Heap_Size 0x00000800

  unsigned char Heap_Mem[Heap_Size] = {0};

  在这里请求的这块内存,在接下来的代码中,被注册进体系中给malloc和free函数所运用:

  __user_initial_stackheap

  LDR R0, = Heap_Mem ; 回来体系中堆内存开始地址

  LDR R1, =(Stack_Mem + Stack_Size)

  LDR R2, = (Heap_Mem + Heap_Size); 回来体系中堆内存的完毕地址

  LDR R3, = Stack_Mem

  BX LR

  就如上面剖析的那样,其实,在裸机编程的时分,对堆内存的办理。并非是智能化的,并非你想请求多少就多少。而是运用一块固定的内存用作堆内存的分配。这在规划的时分,往往不是最佳的计划。这块内存,假如被屡次依照不同的巨细进行请求,就会形成内存碎片。终究导致无法请求到满足的内存。导致体系运转犯错。这在本来内存就现已很少的嵌入式体系中,更是不能承受的。所以,主张是把那个Heap_Size设置成 0 吧。抛弃其运用吧。

  而更为丧命的是,有些malloc,free函数,因为工程人员的偷闲。完成乃至或许如下:

  unsigned char mem_buffer[512];

  unsigned char *mem_offset = & mem_buffer;

  void *malloc(int size)

  {

  unsigned char *tmp = mem_offset;

  mem_offset += size;

  return (void *)tmp;

  }

  void free(void *mem)

  {

  mem_offset = mem;

  }

  2. 更好的代替计划:内存池。

  或许有些同学,觉得:内存池,这是什么东西?

  内存池,简练地来说,便是预先分配一块固定巨细的内存。今后,要请求固定巨细的内存的时分,即可从该内存池中请求。用完了,天然要放回去。留意,内存池,每次请求都只能请求固定巨细的内存。这姿态做,有许多优点:

  (1)每次动态内存请求的巨细都是固定的,能够有用避免内存碎片化。(至于为什么,能够想想,每次请求的都是固定的巨细,收回也是固定的巨细)

  (2)效率高,不需要杂乱的内存分配算法来完成。请求,开释的时刻杂乱度,能够做到O(1)。

  (3)完成简略,易用。

  (4)内存的请求,开释都在可控的规模之内。不会呈现今后运转着,运转着,就再也请求不到内存的状况。

  内存池,并非什么很厉害的技能。完成起来,其实能够做到很简略。只需要一个链表即可。在初始化的时分,把全局变量请求来的内存,一个个放入该链表中。在请求的时分,只需要取出头部并回来即可。在开释的时分,只需要把该内存刺进链表。以下是一种简略的比如(运用移植来的linux内核链表,对该链表的移植,今后有时刻再去剖析):

  #define MEM_BUFFER_LEN 5 //内存块的数量

  #define MEM_BUFFER_SIZE 256 //每块内存的巨细

  //内存池的描绘,运用联合体,表现贫民的才智。就如,我一同学说的:一个字节,恨不能掰成8个字节来用。

  typedef union mem {

  struct list_head list;

  unsigned char buffer[MEM_BUFFER_SIZE];

  }mem_t;

  static union mem gmem[MEM_BUFFER_LEN];

  LIST_HEAD(mem_pool);

  //分配内存

  void *mem_pop()

  {

  union mem *ret = NULL;

  psr_t psr;

  psr = ENTER_CRITICAL();

  if(!list_empty(&mem_pool)) { //有可用的内存池

  ret = list_first_entry(&mem_pool, union mem, list);

  //printf("mem_pool = 0x%p ret = 0x%p\n", &mem_pool, &ret->list);

  list_del(&ret->list);

  }

  EXIT_CRITICAL(psr);

  return ret;//->buffer;

  }

  //收回内存

  void mem_push(void *mem)

  {

  union mem *tmp = NULL;

  psr_t psr;

  tmp = (void *)mem;//container_of(mem, struct mem, buffer);

  psr = ENTER_CRITICAL();

  list_add(&tmp->list, &mem_pool);

  //printf("free = 0x%p\n", &tmp->list);

  EXIT_CRITICAL(psr);

  }

  //初始化内存池

  void mem_pool_init()

  {

  int i;

  psr_t psr;

  psr = ENTER_CRITICAL();

  for(i=0; i

  list_add(&(gmem[i].list), &mem_pool);

  //printf("add mem 0x%p\n", &(gmem[i].list));

  }

  EXIT_CRITICAL(psr);

  }

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/news/guandian/158076.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部