代码之家  ›  专栏  ›  技术社区  ›  Frames Catherine White

在嵌入式系统上使用动态数据结构有多糟糕?

  •  14
  • Frames Catherine White  · 技术社区  · 15 年前

    因此,在我明年在uni学习的嵌入式系统单元中,我们将了解到,在嵌入式系统程序中,动态数据结构是一件不好的事情。 但课堂讲稿没有说明原因。

    现在我正在开发一个中等规模的嵌入式系统“lurc”控制器,主要是利用AVR169Mega“蝴蝶”演示板的外围设备。 产生4个脉宽调制信号控制伺服和电子稳定控制。并提供9段液晶屏。

    现在我想不出任何更好的方法来存储指令,因为它们是通过串行方式接收的。 特别是对于那些我需要等待直到接收到未知数据量的事情:例如在LCD屏幕上显示的字符串。

    那么,为什么不在嵌入式系统的微控制器上使用动态数据结构呢? 你只是在一个内存严重受限的环境中,必须确保你的malloc成功吗?

    8 回复  |  直到 15 年前
        1
  •  22
  •   Dipstick    15 年前

    在嵌入式系统中不使用malloc(或等效软件)有很多原因。

    • 正如你提到的,重要的是你不要突然发现自己失去记忆。
    • 碎片-嵌入式系统可以运行数年,这可能会导致碎片造成严重的内存浪费。
    • 不需要。动态内存分配允许您在不同的时间重复使用相同的内存来做不同的事情。嵌入式系统总是做同样的事情(除了启动时)。
    • 速度。动态内存分配要么相对缓慢(随着内存碎片化而变慢),要么相当浪费(例如,伙伴系统)。
    • 如果要对不同的线程和中断使用相同的动态内存,则分配/释放例程需要执行锁定,这可能会导致足够快地为中断提供服务时出现问题。
    • 动态内存分配使得调试非常困难,特别是在嵌入式系统上使用一些有限的/原始的调试工具时。如果你静态地分配东西,那么你就知道东西一直在哪里,这意味着检查东西的状态要容易得多。

    最重要的是,如果您没有动态地分配内存,那么就无法获得内存泄漏。

        2
  •  5
  •   T.E.D.    15 年前

    好吧,许多小的微控制器没有任何像MMU,或一个有一个很好堆的操作系统供您使用。

    对于那些需要记忆的人来说,只要你保持清醒,我并不认为这有什么大问题。

    然而,许多嵌入式系统也 实时 系统。如果您的应用程序在运行时间上有严格的截止日期,您将遇到动态分配的问题。大多数堆实现使用的算法没有很好的边界运行时。在某些情况下(可能很少见),它们的运行时间会比正常情况下长。有一些实时堆实现,但它们的使用范围并不广。一般规则是避免在硬实时系统初始化后进行任何动态分配或释放。

        3
  •  4
  •   hookenz    15 年前

    这取决于“嵌入”在我看来的意义在过去4年中得到了扩展。

    传统上,嵌入式设备上有微控制器,通常没有操作系统。没有受保护的内存,并且是单线程的。例如,如果只有32kb的内存,那么就很容易耗尽内存,因此必须非常小心处理内存错误。因此,一般来说,我们会使用固定大小的缓冲区编写代码,而从不使用malloc或at(如果每次都使用它的话),这是非常节省的。

    在过去的几年里,我们看到的基本上是单芯片PC或微型板,它们与我们的旧奔腾PC一样强大。RAM的价格现在非常便宜,而且非常小,以至于内存限制与以前完全不同。他们还经常运行嵌入式Linux或WinCE,所以现在我们能够更自由地使用动态内存。

    这是使用更广泛的语言的能力,包括Java、C++、许多脚本语言和其他提供缓冲区溢出保护和异常处理和其他高级语言的语言。所以说真的,那些老问题不像以前那样。

    我怀疑所有这些新的可用硬件都有新的问题。

        4
  •  4
  •   Hayman    15 年前

    在嵌入式环境中,动态内存本身没有什么问题,但在嵌入式环境中,它通常不会给您带来太多好处。

    在我看来,使用环形缓冲区是一个非常好的主意(这是一个非常常见的I/O驱动程序数据结构等)。这样,如果由于某种原因您无法为队列提供服务,那么内存使用仍然是确定的。

    使用一些宏可以在编译时分配可变大小的结构。

    例如

        //we exploit the fact that C doesn't check array indices to allow dynamic alloc of this struct
        typedef struct ring_buf_t {
            int element_sz,
                buffer_sz,
                head,
                tail;
            char data[0];
        } ring_buf_t;
    
       #define RING_BUF_ALLOC_SZ(element_sz,n_elements) (sizeof (ring_buf_t) + \
                                                          (element_sz) * (n_elements))
    
        char backing_buf[RING_BUF_ALLOC_SZ (sizeof(type_to_buffer), 16)];
    
        //ring_buf_init() casts backing buf ring_buf_t and initialises members...
        ring_buf_t *ring_buffer = ring_buf_init (element_sz, n_elemements, backing_buf);
    

    ;

    此模式是一个动态大小的缓冲区,具有保证的内存使用率。当然,其他类型的数据结构(列表、队列等)也可以用同样的方式实现。

        5
  •  3
  •   Erich Kitzmueller    15 年前

    我的印象是,在嵌入式系统中,我确切地知道有多少可用内存,并且我可以完全使用其中的100%;不需要为其他(同时运行)程序留一点内存,但是也没有虚拟内存可以给我101%。所以对于一个队列,我可以很容易地计算出我有981个记录的空间;所以我为这些记录创建了一个数组,如果我需要一个982个记录,我会被打包,必须找到一种优雅地失败的方法。

        6
  •  0
  •   CookieOfFortune    15 年前

    我想说的是记忆力不足和malloc失败的问题。后者更像是一个问题,因为您没有操作系统/接口来帮助系统避免这种故障。使用一个可以让你的整个系统无头运行的功能是非常危险的(或者可能导致重置,仍然很糟糕)。

        7
  •  0
  •   Community CDub    7 年前

    嵌入式系统上的动态数据结构有点像C++中的指针。指针(C++)是邪恶的。但有时它们是唯一的选择;有时它们是较小的邪恶;有时完全避免它们是可以的。如果有 使用它们的一个很好的理由是,有“好”的方法和“坏”的方法可以做到这一点。

    静态分配的变量和数组在分配和解除分配方面要比动态分配的数据快得多,而且访问速度也更快。见 this answer .

    动态分配(我的意思是 malloc() ED或类似)数据也需要 空间 跟踪分配的日常开支。每个分配至少有几个字节-这个空间在嵌入式系统上非常有价值!

    内存泄漏是 大量的 嵌入式系统上的问题,有时可能会运行数年。从这个角度来看,避免动态分配是明智的。

    嵌入式设备通常有相当可靠的规格。你知道传输率是多少,你知道处理信息的速度有多快,等等。在您的示例中,解决方案是使用固定大小的缓冲区作为 circular queue .使缓冲区足够大,可以处理设备需要处理的内容(可能还要多一点)。如果到达的数据太多,可能是由于其他地方的故障或干扰,所以没有太多的保留点,尝试使用所有这些数据。

        8
  •  0
  •   littlegreen    15 年前

    我不知道AtmelMega169,但是我想与169有关的Mega168只有 1024字节 SRAM的。它也只有16K的程序只读存储器,与现代计算机相比相对较慢。所以它在内存、程序大小和速度上都是有限的。

    根据我对AVR汇编程序编程的经验,它需要尽可能多地将功能塞进pic中。使用动态数据结构所需的开销(额外的内存使用、从SRAM中提取和推送数据所需的额外指令、跟踪动态变量驻留在何处、当变量处于“被删除”之间时移动内存块等)并不能证明其优点。

    因此,即使编译器实现了它,为了提高性能,我还是坚持使用静态数据结构。