在PIC中使用malloc


10

如何在PIC中使用malloc()free()运行?我检查了stdlib.h标头,没有任何提及。我正在使用MCC18。

是否有人需要使用它们?

我需要它们,因为我正在将库从Windows XP移植到PIC。移植指南说

使操作系统的特定功能适应我的PIC功能

但是我不知道如何“翻译” malloc()free()功能。


4
如果可能,请尝试使用静态分配。
尼克T 2010年

1
为什么?问题确实是,我正在写一个相当大的库的底层(特定于平台的那一层),并且有很多函数,我不知道它们的用途是什么..而且我也不知道如何从动态到静态..
stef 2010年

11
听起来像是RAM <4KB的PIC单片机可能对您的应用是错误的。在PC上,在开始端口之前测量PC库的内存使用情况。使用功能更强大的ARM Cortex-M3之类的产品可能会更好。经验法则:如果您要移植的代码库太大而无法理解,那么它将不适合PIC。
Toby Jaffey 2010年

Windows驱动程序(和一般的应用程序)基本上是用“无限RAM”范例编写的,因为如果物理RAM用完了,则可以交换虚拟内存。根据库的工作情况,它可能会消耗比4kB更多的内存在您的PIC18F87J11中可用。我怀疑您将无法衡量驱动程序将要使用多少内存。
亚当·劳伦斯

另一个潜在的问题:Win32 int是32位,而使用MCC18编译器时,它只有16位。如果不小心,可能会出现奇怪的溢出问题。
亚当·劳伦斯

Answers:


8

在许多应用程序中,将需要分配内存,而无需在保留已分配的内容的同时释放任何内容。在这样的系统上,所有需要做的就是使用链接器使用所有可用的RAM定义一个数组,设置一个指向该数组开头的指针,然后使用一个不错的简单malloc函数:

char * next_alloc;
无效* malloc(int size)
{
    字符* this_alloc;
    this_alloc = next_alloc;
    如果((END_OF_ALLOC_SPACE-this_alloc)<大小)
      返回-1;
    next_alloc + =大小;
    返回this_alloc;
}
无空(void * ptr)
{
    如果(ptr)
        next_alloc =(char *)ptr;
}

简单易用,任何数量的分配仅需要两个字节的总开销。在块上调用free()将取消分配该块及其后的所有内容。

可以通过使用两个指针来处理稍微复杂一些的分配模式-一个指针从向上移动的内存底部分配内容,另一个指针从向下移动的内存顶部分配内存。如果堆中的数据是同质的并且知道所有外部引用在哪里,也可以使用压缩垃圾收集器。


要complety了解posibilities,读答案在electronics.stackexchange.com/questions/7850/...
gavioto20

14

malloc()在微控制器中通常被认为是“坏事”。但是,如果您绝对需要它,那么您将需要找到第三方版本。

如果幸运的话,要移植的代码可能不依赖于重用内存块。在这种情况下,您可以编写一个简单的分配器,该分配器将指针返回到RAM缓冲区中,然后将指针前进所需的块大小。

在将PC库移植到微控制器之前,我已经成功使用了这种方法。

在下面,您将使用设置分配器,my_malloc_init()并使用分配内存my_malloc()my_free()是否在那里满足依赖关系,但实际上什么也不会做。最终,您当然会用完空间。

要使此工作正常进行,您需要测量代码的最坏情况下的内存需求(如果可能,请在PC上执行此操作),然后进行相应的设置HEAP_SIZE。在进入需要动态内存的库部分之前,请致电my_malloc_init()。在重复使用之前,请确保没有任何东西指向heap

uint8_t heap[HEAP_SIZE];
uint8_t *heap_ptr;

void my_malloc_init(void)
{
    heap_ptr = heap;
}

void *my_malloc(size_t len)
{
    uint8_t *p = heap_ptr;
    heap_ptr += len;
    if (heap_ptr >= heap + HEAP_SIZE)
        return NULL;
    else
        return p;
}

void my_free(void)
{
    // do nothing
}

(注意:在现实世界中,您可能需要考虑指针对齐,即四舍五入heap_ptr为2或4个字节)

另一种选择是使用比malloc()通常提供的更简单的分配结构,例如FreeList,尽管这可能不允许您分配可变大小的块。


3
我不知道什么时候malloc被认为是嵌入式的好东西。
Kellenjb 2010年

1
我仍然同意您不希望像其他人所说的那样在程序中进行动态分配,但这是实现这一目标的一种好方法。到目前为止,为嵌入式设计的第三方malloc是最佳选择。必须避免分段。@jobyTaffey写得好。
Kortuk

1
@Kellenjb好吧,这是一个全新的问题:-)
Toby

1
我建议my_free应该将heap_ptr设置为传入的值,从而有效地释放所指示的项以及在其之后分配的所有内容。显然,必须以允许这种用法的顺序分配事物,但是这种模式并不少见。另一个有用的变化是具有两对alloc / free函数,其中一对分配自上而下的分配,另一对分配自下而上的分配。
超级猫

13

这几乎不能回答您的问题,但是动态内存分配通常在小型RAM环境中并且在没有操作系统(例如在微控制器世界中)的情况下不受欢迎。通常,嵌入式环境中可用的堆空间通常是以数百个字节为单位...

实现malloc和free本质上是对“空闲段”结构的链表的维护,并且您可以想象,与空闲段相关联的元数据与通常可用的内存量相比并不算是虚假的……这是“开销”管理动态内存池”会消耗大量可用资源。


在某些实现中,元数据开销非常小。对于分配的块,您仅需要其大小。对于未使用的块,即使具有相当合理的最小块大小,通常也可以免费使用链接列表指针。
恢复莫妮卡

使用微控制器的小型,长期运行的系统的问题通常与元数据无关,而与内存碎片有关。更糟糕的是,对代码进行小的更改可能会导致以前没有内存碎片的内存碎片,因此您可能会做出看起来很无辜的更改,从而突然使程序停止“过早地工作”。
恢复莫妮卡

11

我不知道C18标准库是否支持mallocfree,但是Microchip应用笔记AN914展示了如何实现自己的。

在任何情况下,托马斯和其他海报建议,采用动态内存的PIC与他们的非常小的RAM空间充满了危险。由于缺少更高级的虚拟内存管理器,而这些虚拟内存管理器无法充分发挥操作系统的作用,从而导致分配失败和崩溃,因此您可能会很快耗尽连续的空间。更糟糕的是,它可能不是确定性的,并且可能很难调试。

如果您正在做的事情是在运行时真正动态确定的(对于大多数嵌入式事物来说是很少的),并且您只需要在几个非常特殊的情况下分配空间,我就会发现malloc并且free可以接受。


用完连续空间(也就是堆碎片)是一个问题,它完全独立于地址空间的大小以及是否具有虚拟内存。您可能需要权衡一些浪费的RAM以减少堆碎片,但是最终,在长时间运行的系统上,您无法保证不会耗尽堆空间。小型系统和大型系统之间的唯一区别是,磁盘开始抖动需要多长时间(在具有分页VM的系统上),或者分配器返回NULL(在嵌入式系统中)。
恢复莫妮卡

@KubaOber:通常可以保证一定大小的RAM将能够处理分配和释放操作的任何顺序,而这些顺序永远不需要多于一定数量(较小)的RAM来同时进行分配。嵌入式系统的问题在于,即使在最坏的情况下,要保证成功也将需要比没有分段所需的RAM多得多的RAM。
超级猫

@supercat你是对的。确实,我太过戏剧化了。有这些保证的正式证明。
恢复莫妮卡

2

那么,您的PIC在内存方面有多大?

malloc是分配内存的一种非常低效的方式。这样做的问题是,频繁的释放和malloc可能使内存变得碎片化,而只有几千字节的内存,分配失败非常普遍。很可能,如果您使用的是较小的芯片或较早的PIC18,则不支持malloc,因为Microchip认为实现起来非常困难(在某些情况下甚至是不可能的),或者使用起来不够用。值得。更不用说了,但是它也相当慢,您正在寻找使用一个可用的静态缓冲区的1个周期,以及执行malloc的100至1,000个周期。

如果要静态分配,则创建诸如sprintf函数的缓冲区(如果有的话,大约128字节),SD卡的缓冲区(如果有的话)之类的东西,直到您不再需要malloc。理想情况下,仅在绝对需要它的地方使用它,并且无法摆脱静态分配,但是这些情况通常很少见,这也许表明您应该考虑使用更大或更强大的微控制器。

而且,如果您正在PIC18上开发/移植“操作系统”,并且如果它支持微控制器,则它可能支持静态分配。例如,SQLite3支持静态分配-您为它分配一个大的缓冲区数组,即使不是微控制器,它也会在可能的情况下使用它。如果不是,那么您确定它是为小型PIC18设计的吗?


我明白你的意思了。我正在使用PIC18F87J11,它具有128K的ram,足够吗?
stef 2010年

Stefano,该芯片具有3,904字节的RAM。它具有128K的程序闪存。
W5VO 2010年

@Stefao Salati-3.8KB很小。
Thomas O 2010年

对不起..您认为这还足够吗?
stef 2010年

@Stefano Salati,无需道歉。我认为您确实会推动它。它可能会起作用,但是会浪费大量性能和可用内存。
Thomas O 2010年

2

如果你正在考虑malloc(),并free()为您的嵌入式软件,我建议你看一看的uC / OS-II和OSMemGet()OSMemPut()。虽然malloc()可以分配任意的内存块,但OSMem*()可以从预分配的池中分配固定大小的块。我发现这种方法malloc()在静态分配的灵活性和鲁棒性之间取得了很好的平衡。


0

AFAIK,要正确执行此操作,您确实需要查看具有某种内存管理单元(MMU)的设备。尽管确实存在PIC18系列的动态分配机制,但它们的作用并不会那么牢固-就像说有人致力于突破PIC18系列极限的固件一样,我可以说您不会如果您将所有开销都花在了内存管理器上,那么其中就有一个相当大的应用程序。

更好的解决方案:尝试了解它在做什么以及为什么需要动态分配。查看您是否无法重构它以使其与静态分配一起使用。(在某些情况下,这根本是不可能的-如果库/应用程序被设计为执行可自由缩放的操作,或者没有可以接受的输入量的界限。)但是有时,如果您确实认为关于您要执行的操作,您可能会发现有可能(甚至可能很容易)使用静态分配。


1
你不对 MMU允许您与外部存储器进行接口(可能比PIC上的4kB更多)。带有和不带有MMU的动态和静态分配几乎没有区别。一旦开始进入虚拟内存,就存在区别,但这仅与malloc切向相关。
凯文·维米尔

1
尽管早期的Macintosh计算机没有MMU,但早期的Macintosh程序员还是经常使用malloc()和free()(或Pascal等效项)。在我看来,“正确”使用malloc()需要MMU的想法似乎不正确。
davidcary 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.