Chinaunix首页 | 论坛 | 博客
  • 博客访问: 805601
  • 博文数量: 281
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2770
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-02 19:45
个人简介

邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛

文章分类
文章存档

2020年(1)

2018年(1)

2017年(56)

2016年(72)

2015年(151)

分类: 嵌入式

2015-09-28 13:29:53

概览
每当任务,队列或是信号量被创建时,内核需要进行动态内存分配。虽然可以调用标准的malloc()与free()库函数,但必须承担以下若干问题:
1. 这两个函数在小型嵌入式系统中可能不可用。
2. 这两个函数的具体实现可能会相对较大,会占用较多宝贵的代码空间。
3. 这两个函数通常不具备线程安全特性。
4. 这两个函数具有不确定性。每次调用时的时间开销都可能不同。
5. 这两个函数会产生内存碎片。
6. 这两个函数会使得链接器配置得复杂。
不同的嵌入式系统具有不同的内存配置和时间要求。所以单一的内存分配算法只可能适合部分应用程序。因此,FreeRTOS 将内存分配作为可移植层面(相对于基本的内核代码部分而言)。这使得不同的应用程序可以提供适合自身的具体实现。

当内核请求内存时,其调用pvPortMalloc()而不是直接调用malloc();当释放内存时,调用vPortFree()而不是直接调用free()。pvPortMalloc()具有与malloc()相同的函数原型;vPortFree()也具有与free()相同的函数原型。

目前源代码中,FreeRTOS 自带有5种pvPortMalloc()与vPortFree()实现范例,FreeRTOS 的用户可以选用其中一种,也可以采用自己的内存管理方式。在文件下:\FreeRTOS\Source\portable\MemMang,可以找到。

在小型嵌入式系统中,通常是在启动调度器之前创建任务,队列和信号量。这种情况表明,动态分配内存只会出现在应用程序真正开始执行实时功能之前,而且内存一旦分配就不会再释放。这就意味着选择内存分配方案时不必考虑一些复杂的因素,比如确定性与内存碎片等,而只需要从性能上考虑,比如代码大小和简易性。

内存分配案例:

Heap_1.c
Heap_1.c 实现了一个非常基本的pvPortMalloc()版本,而且没有实现vPortFree()。如果应用程序不需要删除任务,队列或者信号量,则具有使用heap_1 的潜质。Heap_1总是具有确定性。
这种分配方案是将FreeRTOS 的内存堆空间看作一个简单的数组。当调用pvPortMalloc()时,则将数组又简单地细分为更小的内存块。
数组的总大小(字节为单位)在FreeRTOSConfig.h 中由configTOTAL_HEAP_SIZE定义。以这种方式定义一个巨型数组会让整个应用程序看起来耗费了许多内存——即使是在数组没有进行任何实际分配之前。
需要为每个创建的任务在堆空间上分配一个任务控制块(TCB)和一个栈空间。下图展示了heap_1 是如何在任务创建时细分这个简单数组的。从图中可以看到:
? A 表示数组在没有任何任务创建时的情形,这里整个数据是空的。
? B 表示数组在创建了一个任务后的情形。
? C 表示数组在创建了三个任务后的情形。


Heap_2.c
Heap_2.c 也是使用了一个由configTOTAL_HEAP_SIZE 定义大小的简单数组。不同于heap_1 的是,heap_2 采用了一个最佳匹配算法来分配内存,并且支持内存释放。
由于声明了一个静态数组,所以会让整个应用程序看起来耗费了许多内存——即使是在数组没有进行任何实际分配之前。
最佳匹配算法保证pvPortMalloc()会使用最接近请求大小的空闲内存块。比如,考虑以下情形:
1. 堆空间中包含了三个空闲内存块,分别为5 字节,25 字节和100 字节大小。
2. pvPortMalloc()被调用以请求分配20 字节大小的内存空间。

匹配请求字节数的最小空闲内存块是具有25字节大小的内存块——所以pvPortMalloc()会将这个25 字节块再分为一个20 字节块和一个5 字节块3,然后返回一个指向20 字
节块的指针。剩下的5 字节块则保留下来,留待以后调用pvPortMalloc()时使用。

Heap_2.c 并不会把相邻的空闲块合并成一个更大的内存块,所以会产生内存碎片——如果分配和释放的总是相同大小的内存块,则内存碎片就不会成为一个问题。
Heap_2.c 适合用于那些重复创建与删除具有相同栈空间任务的应用程序。

下图展示了当任务创建,删除以及再创建过程中,最佳匹配算法是如何工作的。
从下图中可以看出:
? A 表示数组在创建了三个任务后的情形。数组的顶部还剩余一个大空闲块。
? B 表示数组在删除了一个任务后的情形。顶部的大空闲块保持不变,并多出了两个小的空闲块,分别是被删除任务的TCB 和任务栈。
? C 表示数组在又创建了一个任务后的情形。创建一个任务会产生两次调用pvPortMalloc(),一次是分配TCB,一次是分配任务栈(调用pvPortMalloc()发生在xTaskCreate() API 函数内部)。
每个TCB 都具有相同大小,所以最佳匹配算法可以确保之前被删除的任务占用的TCB 空间被重新分配用作新任务的TCB 空间。
新建任务的栈空间与之前被删除任务的栈空间大小相同,所以最佳匹配算法会保证之前被删除任务占用的栈空间会被重新分配用作新任务的栈空间。数组顶部的大空闲块依然保持不变。
Heap_2.c 虽然不具备确定性,但是比大多数标准库实现的malloc()与free()更有效率。

Heap_3.c
Heap_3.c 简单地调用了标准库函数malloc()和free(),但是通过暂时挂起调度器使得函数调用备线程安全特性。其实现代码参见程序。
此时的内存堆空间大小不受configTOTAL_HEAP_SIZE 影响,而是由链接器配置决定。

void *pvPortMalloc( size_t xWantedSize )
{
void *pvReturn;

    vTaskSuspendAll();
    {
        pvReturn = malloc( xWantedSize );
        traceMALLOC( pvReturn, xWantedSize );
    }
    ( void ) xTaskResumeAll();

    #if( configUSE_MALLOC_FAILED_HOOK == 1 )
    {
        if( pvReturn == NULL )
        {
            extern void vApplicationMallocFailedHook( void );
            vApplicationMallocFailedHook();
        }
    }
    #endif

    return pvReturn;
}
/*-----------------------------------------------------------*/

void vPortFree( void *pv )
{
    if( pv )
    {
        vTaskSuspendAll();
        {
            free( pv );
            traceFREE( pv, 0 );
        }
        ( void ) xTaskResumeAll();
    }
}





参考贴:
FreeRTOS内存管理
【FreeRTOS代码剖析之2:内存管理Heap_2.c

阅读(1982) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~