第八講 內存管理zz

發信人: gdtyy (gdtyy), 信區: Embedded
標  題: 第八講 內存管理
發信站: 水木社區 (Mon Jun 25 23:33:26 2007), 站內

*******************
* 第八講 內存管理 *
*******************
    2007/02/03  [email protected]  www.armecos.com

    在嵌入式系統中,爲了對內存資源進行很好地控制,避免發生不可預測的後果,一般建
議使用靜態內存分配。ecos是專門爲實時嵌入式系統量身定做的操作系統,它的大多數系統
調用在被使用時都要求將預先指定的由系統調用所產生的目標對象的內存地址傳遞給該系統
調用(即對其進行靜態內存分配)。如第一講cyg_thread_create系統調用函數就要求傳入句
柄指針和線程數據結構體指針及堆棧起址,而這三個指針對應的變量都是預先分配的靜態變
量。如下:
    static char stack[4][STACK_SIZE];
    static cyg_thread thread_data[4];
    static cyg_handle_t thread_handle[4];

    cyg_thread_create(10,                // Priority - just a number
                      taska,             // entry
                      1,                 // entry parameter
                      "taska",           // Name
                      &stack[1],         // Stack                 <-------------
                      STACK_SIZE,        // Size
                      &thread_handle[1], // Handle                <-------------
                      &thread_data[1]    // Thread data structure <-------------
            );

    對比ucos for 51中類似的創建任務系統調用,可見,兩者的內核數據和堆棧空間都是
靜態分配的。不過,ecos更靈活,它的內核數據存儲空間也由應用程序提供,實際用多少變
量就分配多少空間,而ucos是在配置內核時預先定死的,不夠靈活。
    #define OS_MAX_TASKS             27
    OS_STK TaskStartStkyya[MaxStkSize];//注意:我在ASM文件中設置?STACK空間爲40H
即64。
    OSTaskCreate(TaskStartyya, (void *)0, &TaskStartStkyya[0],2);

    ecos是一個單進程多線程系統,作爲一個嵌入式可配置操作系統,它的內存管理相對簡
單,不分段也不分頁,沒有存儲保護,直接映射成一個平板內存。ecos採用一種基於內存池
的動態內存分配機制,這是由uITRON兼容層實現的一種靈活有效的內存管理方式。
    在實際使用時,內存可能不連續,ecos通過定義多個堆段自動分配內存池,不過,不連
續內存的管理會增加開銷,最好不用這種不連續的堆。開發板有一塊內存是CPU內部的,還
有一塊是外部的,速度、容量、成本不同,可以把常用數據存入內部RAM以加快處理速度。
    在一些特殊情況下,某些系統具有現場增加內存的支持能力,這種情形要求對這些內存
進行自動分配。爲此,在硬件抽象層中(hal_intr.h)有一個宏定義:
HAL_MEM_REAL_REGION_TOP(cyg_uint8 *regionend),該宏採用正常情況下的內存末端地址
作爲參數。它返回一個由HAL實時檢測到的實際內存末端地址。通過使用該宏,可以靈活地
對多個內存區進行操作。
    ecos允許對擁有的所有可用內存的內存池進行自動定義,可以自動分配堆的大小。
    ecos提供了兩種內存池:一種是變長內存池(variable size memory pool),根據申請
的大小進行分配;另一種是定長內存池(fixed size memory pool),以固定大小的塊爲單位
進行分配。變長內存池使用鏈表來進行管理,定長內存池使用位圖來進行管理。C庫函數
malloc使用變長內存池實現內存分配,用戶可以直接使用C庫函數malloc和free管理內存。
    下面分別介紹兩種內存池對應的API函數使用方法:(函數定義位於頭文件
<cyg/memalloc/kapi.h>內)

    抽象出來的內存操作主要包括:創建、刪除、分配(阻塞/超時阻塞/非阻塞)、釋放、查
詢等待、查詢信息。

***********************
* 固定長度內存分配API *
***********************
1、創建內存池

    void cyg_mempool_fix_create(堆起址,堆大小,內存塊大小,返回的內存池句柄,固
定內存池結構體)

    ecos函數的命名都是有特點的,就象前面講過的一樣,cyg表示cygnus公司出品,
mempool表示這個函數是與內存池相關的,fix表示是固定長度內存池,create顧名思義是創
建固定長度內存池,很好記吧,即使忘了也很容易“蒙”出來。
    該函數產生一個可進行固定大小內存分配的定長內存池。堆大小不一定是整個可用內存
的大小。定長內存池在速度上要優於變長的。新產生的內存池可以通過句柄對其進行訪問。

2、刪除內存池

    void cyg_mempool_fix_delete(內存池句柄)

    該函數刪除定長內存池。不要刪除正在使用的內存池,否則會引起系統錯誤。

3、分配內存(帶阻塞)

    void * cyg_mempool_fix_alloc(句柄)

4、分配內存(帶超時阻塞)

    void * cyg_mempool_fix_timed_alloc(句柄,超時值)

5、分配內存(非阻塞)

    void * cyg_mempool_fix_try_alloc(句柄)

6、釋放內存

    void cyg_mempool_fix_free(句柄,要釋放回內存池的內存的指針)

7、檢查是否有線程正等待分配內存

    cyg_bool_t cyg_mempool_fix_waiting(句柄)

8、取得內存池信息

    void cyg_mempool_fix_get_info(句柄,要返回的信息結構指針)

***********************
* 可變長度內存分配API *
***********************
與固定長度內存分配API類似,只是把fix換成了var,注意參數不一樣了。

1、創建內存池

    void cyg_mempool_var_create(堆起址,堆大小,返回的內存池句柄,固定內存池結構
體)

2、刪除內存池

    void cyg_mempool_var_delete(內存池句柄)

    該函數刪除定長內存池。不要刪除正在使用的內存池,否則會引起系統錯誤。

3、分配內存(帶阻塞)

    void * cyg_mempool_var_alloc(句柄,要分配的內存塊大小)

4、分配內存(帶超時阻塞)

    void * cyg_mempool_var_timed_alloc(句柄,要分配的內存塊大小,超時值)

5、分配內存(非阻塞)

    void * cyg_mempool_var_try_alloc(句柄,要分配的內存塊大小)

6、釋放內存

    void cyg_mempool_var_free(句柄,要釋放回內存池的內存的指針)

7、檢查是否有線程正等待分配內存

    cyg_bool_t cyg_mempool_var_waiting(句柄)

8、取得內存池信息

    void cyg_mempool_var_get_info(句柄,要返回的信息結構指針)


注意不定長分配內存時爲了避免小碎片影響系統性能,應限定可變內存申請的最小值爲一個
比較大的數。
--------------------------------------------------------------------------------
--------
下面給出lwip在ecos上移植的sys_arch中的函數實現,有興趣的讀者可以對照分析lwip內存
管理和ecos內存管理的對應關係。
--------------------------------------------------------------------------------
--------
//這個文件實現了lwIP使用的針對eCos平臺移植的sys_arch函數

#include "lwip/opt.h"
#include "arch/sys_arch.h"
#include "lwip/sys.h"
#include "lwip/def.h"

#define tick_to_msec(tick)    ((u16_t)((tick)*10+1))
#define msec_to_tick(msec)    ((cyg_tick_count_t)(msec+9)/10)

//我們使用一個通用的可變長內存池來分配信號量、信箱和線程所需內存塊
static char memvar[CYGNUM_LWIP_VARMEMPOOL_SIZE];//可變長內存池實體
static cyg_mempool_var var_mempool;//內存池結構體變量,詳見kapidata.h.
static cyg_handle_t var_mempool_h;//可變長內存池句柄

#define SYS_THREADS    2    /* polling thread and tcpip_thread */

#define THREAD_COUNT    (CYGNUM_LWIP_APP_THREADS + SYS_THREADS)
static char memfix[CYGNUM_LWIP_THREAD_STACK_SIZE * THREAD_COUNT];//所有線程堆棧
空間

//threads鏈表:包含lwIP超時信息的eCos線程信息。
struct lwip_thread {
    struct lwip_thread * next;
    struct sys_timeouts to;
    cyg_handle_t th;
    cyg_thread t;  //線程數據存儲空間
} *threads;

/*
 * Timeout for threads which were not created by sys_thread_new
 * usually "main"
 */
struct sys_timeouts to;

//建立內存池,初始化thread和to。
void sys_init(void)
{
    cyg_mempool_var_create(memvar, sizeof(memvar), &var_mempool_h, &var_mempool)
;

    threads = NULL;
    to.next = NULL;
}

//創建一個新郵箱。如果內存不夠,返回NULL。
sys_mbox_t sys_mbox_new(void)
{
    cyg_mbox * mbox;
    cyg_handle_t m;
    mbox = (cyg_mbox *)cyg_mempool_var_try_alloc(var_mempool_h, sizeof(cyg_mbox)
);

    if(!mbox)
        return SYS_MBOX_NULL;

    //m和mbox實際是一樣的
    cyg_mbox_create(&m, mbox);
    return m;
}

//銷燬郵箱並釋放其佔用的內存空間。
void sys_mbox_free(sys_mbox_t mbox)
{
    if (mbox != SYS_MBOX_NULL) {
        cyg_mbox_delete(mbox);
        cyg_mempool_var_free(var_mempool_h,(void*)mbox);
    }
}

//cyg_mbox_put不能傳送NULL,否則cyg_mbox_get將不能區分到底是真實數據還是錯誤條件

//但是,lwIP確實在某些適於使用信號量的時候傳遞NULL。所以要用&dummy_msg代替NULL。
int dummy_msg = 1;

//發送消息到信箱
void sys_mbox_post(sys_mbox_t mbox, void *data)
{
    if (!data)  //cyg_mbox_put的消息不能爲NULL,用&dummy_msg代替。
        data = &dummy_msg;
    while (cyg_mbox_put(mbox,data) == false);
}

#if 0  //註釋用
void
sys_mbox_fetch(sys_mbox_t mbox, void **msg){
    void *d;
    d = cyg_mbox_get(mbox);
    if (msg)
        *msg = d;
}
#endif

//超時等待信箱,超時返回-1,正常返回等待ms數。
u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void **data, u32_t timeout)
{
    void *d;
    cyg_tick_count_t end_time = 0, start_time = 0;
    if (timeout) {//超時等待信箱
        start_time = cyg_current_time();
        d = cyg_mbox_timed_get(mbox, start_time + msec_to_tick(timeout));
        end_time = cyg_current_time();

        //超時
        if (d == NULL)
            return SYS_ARCH_TIMEOUT;
    }
    else{//永遠等待信箱
        d = cyg_mbox_get(mbox);
    }

    if (data) {
        if (d == (void *)&dummy_msg)
            *data = NULL;
        else
            *data = d;
    }
    //返回延時ms數
    return tick_to_msec(end_time - start_time);
}


//分配一個新信號量並初始化,如果沒有空間可以提供則返回NULL
sys_sem_t sys_sem_new(u8_t count)
{
    sys_sem_t sem;   //????sys_sem_t*???

    //從內存池分配可變長內存塊,如無空間則立即返回NULL,否則返回新指針。
    sem = (cyg_sem_t *)cyg_mempool_var_try_alloc(var_mempool_h,
sizeof(cyg_sem_t));
    if(!sem)
        return SYS_SEM_NULL;
    cyg_semaphore_init(sem, count);
    return sem;
}

#if 0  //註釋用
void
sys_sem_wait(sys_sem_t sem)
{
    cyg_semaphore_wait(sem);
}

void
sys_timeout(u16_t msecs, sys_timeout_handler h, void *arg)
{}
#endif

//超時等待一個信號量,如果超時返回-1,否則返回等待時間.
u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout)
{
    cyg_bool_t r;
    cyg_tick_count_t end_time = 0, start_time = 0;

    if (timeout) {//帶延時等待信號量
        start_time = cyg_current_time();
        r = cyg_semaphore_timed_wait(sem, start_time + msec_to_tick(timeout));
        end_time = cyg_current_time();

        if (r == false) {//超時
            return SYS_ARCH_TIMEOUT;
        }
    }
    else{//永遠等待信號量
        cyg_semaphore_wait(sem);
    }
    //收到信號量,返回延時ms數
    return tick_to_msec(end_time - start_time);
}

//發送信號量
void sys_sem_signal(sys_sem_t sem)
{
    cyg_semaphore_post(sem);
}

//銷燬信號量並釋放其所佔用的內存空間
void sys_sem_free(sys_sem_t sem)
{
    //銷燬信號量
    cyg_semaphore_destroy(sem);
    //釋放內存,內存池,分配時返回指針
    cyg_mempool_var_free(var_mempool_h,(void*)sem);
}

//創建新線程(線程函數,參數,優先級)
//thread_create(優先級,線程實體函數,線程參數,線程名字,堆棧基址,堆棧大小,返
回的線程句柄,線程數據存儲空間)
sys_thread_t sys_thread_new(void (*function) (void *arg), void *arg,int prio)
{
    struct lwip_thread * nt;
    void * stack;
    static int thread_count = 0;
    //可變長度內存分配,內存池,大小
    nt = (struct lwip_thread *)cyg_mempool_var_alloc(var_mempool_h,
sizeof(struct lwip_thread));

    nt->next = threads;
    nt->to.next = NULL;

    threads = nt;

    //堆棧起址爲memfix+每個堆棧大小*線程數++
    stack = (void *)(memfix+CYGNUM_LWIP_THREAD_STACK_SIZE*thread_count++);
    cyg_thread_create(prio, (cyg_thread_entry_t *)function, (cyg_addrword_t)arg,
        (char *)arg , stack, CYGNUM_LWIP_THREAD_STACK_SIZE, &(nt->th), &(nt->t)
);

    cyg_thread_resume(nt->th);
    return NULL;
}

//返回當前線程的timeouts信息結構指針
struct sys_timeouts *sys_arch_timeouts(void)
{
    cyg_handle_t ct;
    struct lwip_thread *t;

    ct = cyg_thread_self();
    for(t = threads; t; t = t->next)
        if (t->th == ct)
            return &(t->to);
    return &to;
}

--

※ 來源:·水木社區 http://newsmth.net·[FROM: 61.149.56.*]

發佈了29 篇原創文章 · 獲贊 6 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章