每次 溫故uc/os-ii操作系統的源碼都有收獲,經典的東西就是經典;uc/os-ii曾經把我搞得好累,但是經過一段時間摸索之后發現只要有正確的學習方法,學好uc/os-ii并不難,同時基于uc/os-ii操作系統的應用程序開發也不難。關于嵌入式系統內存管理的方法應該不少,原子的內存管理教程就有兩個例程,一個是“特大號數組”型,一個是動態分配型,兩者都使用了malloc()函數。而uc/os-ii操作系統的內存管理實現原理很精妙沒有使用malloc()函數,主要是考慮該函數申請內存的時間不確定性。可以說,通過該例程就能理解uc/os-ii操作系統的內存管理實現原理。筆者認為通過實例學習,理解uc/os-ii操作系統的原理是個不錯的選擇。
本實驗基于PC機在DOS環境下模擬。
/*設計一個有3個任務的應用程序,這3個任務分別是Mytask Youtask Hertask。在應用程序中創建一個動態內存分區,該分區有8個內存塊,每個內存塊的長度是6字節。應用程序的任務Youtask Hertask都在任務運行后請求一個內存塊.隨后就釋放它;任務MYTASK也在任務運行后請求一個內存塊,但是要在任務MYTASK運行6次后,才釋放它所申請的內存塊。為了了解內存分區變化的情況,編寫代碼來觀察分區頭指針和已被使用內存塊的個數*/
#include "INCLUDES.h"
#define TASK_STK_SIZE 512 /* 任務堆棧長度*/
OS_STK StartTaskStk[TASK_STK_SIZE]; /*定義任務堆棧區 */
OS_STK MyTaskStk[TASK_STK_SIZE];
OS_STK YouTaskStk[TASK_STK_SIZE];
OS_STK HerTaskStk[TASK_STK_SIZE];
char *s;
char *s1= "Mytask ";
char *s2= "Youtask ";
char *s3= "Hertask ";
INT8U err;
INT8U y=0; //字符顯示位置
INT8U Times=0;
OS_MEM *IntBuffer; /*定義內存控制塊指針,創建一個內存分區時,返回值就是它 */
INT8U IntPart[8][6]; /*劃分一個具有8個內存塊,每個內存塊長度是6的內存分區 */
INT8U *IntBlkPtr; /*定義內存塊指針,確定內存分區中首個內存塊的指針 */
OS_MEM_DATA MemInfo; /*存放內存分區的狀態信息 */
void StartTask(void *data); /* 聲明起始任務 */
void MyTask(void *data); /* 聲明任務 */
void YouTask(void *data); /* 聲明任務 */
void HerTask(void *data); /* 聲明任務 */
/*
********************************************************************
* MAIN主函數
**********************************************************************
*/
void main (void)
{
OSInit(); /* 初始化uC/OS-II */
PC_DOSSaveReturn(); /* 保存DOS環境 */
PC_VectSet(uCOS, OSCtxSw); /* 安裝uC/OS-II的中斷 */
IntBuffer=OSMemCreate(IntPart,8,6,&err); /*創建動態內存區 */
OSTaskCreate(StartTask, (void *)0, &StartTaskStk[TASK_STK_SIZE - 1], 0);
OSStart(); /* 啟動多任務管理 */
}
/*
***********************************************************
* STARTUP TASK:主要功能就是創建3個任務
***********************************************************
*/
void StartTask(void *pdata)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register,實際應用中該部分可省略 */
OS_CPU_SR cpu_sr;
#endif
INT16S key; /*用于退出的建*/
pdata = pdata; /* Prevent compiler warning */
OS_ENTER_CRITICAL();
PC_VectSet(0x08, OSTickISR); /* 安裝時鐘中斷向量 , */
PC_SetTickRate(OS_TICKS_PER_SEC); /* 設置時鐘頻率 */
OS_EXIT_CRITICAL();
OSStatInit(); /* 初始化統計任務 */
OSTaskCreate(MyTask, (void *)0, &MyTaskStk[TASK_STK_SIZE - 1], 3);
OSTaskCreate(YouTask, (void *)0, &YouTaskStk[TASK_STK_SIZE - 1], 4);
OSTaskCreate(HerTask, (void *)0, &HerTaskStk[TASK_STK_SIZE - 1], 5);
for (;;) {
//如果恩下ESC鍵,則退出UC/OS-II
if (PC_GetKey(&key) == TRUE) { /* See if key has been pressed */
if (key == 0x1B) { /* Yes, see if it's the ESCAPE key */
PC_DOSReturn(); /* Return to DOS */
}
}
OSTimeDlyHMSM(0, 0, 3, 0); /* Wait 3s,在這里創建完StartTask任務為什么不掛起自己? */
}
}
/*--------------------MyTask任務-------------------------*/
void MyTask(void *pdata)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
pdata=pdata;
for( ; ; )
{
PC_DispStr(10,++y,s1,DISP_BGND_BLACK+DISP_FGND_WHITE); /*"++y"執行一次就換一次行 */
IntBlkPtr=OSMemGet( /*請求內存分區的內存塊的指針 */
IntBuffer, /*這個參數就指明了要獲取的內存塊屬于哪個內存分區 */
&err);
OSMemQuery( /*查詢內存控制塊信息 */
IntBuffer, /*帶查詢內存控制塊指針 */
&MemInfo);
sprintf(s,"%0x",MemInfo.OSFreeList); /*顯示頭指針 */
PC_DispStr(30,y,s,DISP_BGND_BLACK+DISP_FGND_WHITE);
sprintf(s,"%d",MemInfo.OSNUsed); /*顯示已用的內存塊數目 */
PC_DispStr(40,y,s,DISP_BGND_BLACK+DISP_FGND_WHITE);
if(Times>4) /*0-1-2-3-4-5;Times==6時該條件語句為真 */
{
OSMemPut(IntBuffer,IntBlkPtr); /* 進入第6次就釋放已經使用過的內存塊 */
}
Times++;
OSTimeDlyHMSM(0,0,1,0); //等待2s
}
}
/*-------------------------------Youtask-------------------------------------*/
void YouTask(void *pdata)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
pdata=pdata;
for( ; ; )
{
PC_DispStr(10,++y,s2,DISP_BGND_BLACK+DISP_FGND_WHITE);
IntBlkPtr=OSMemGet( //請求內存塊
IntBuffer, //內存分區的指針
&err); //錯誤信息
OSMemQuery( //查詢內存控制塊信息
IntBuffer, //帶查詢內存控制塊指針
&MemInfo);
sprintf(s,"%0x",MemInfo.OSFreeList); //顯示頭指針
PC_DispStr(30,y,s,DISP_BGND_BLACK+DISP_FGND_WHITE);
sprintf(s,"%d",MemInfo.OSNUsed); //顯示已用的內存塊數目
PC_DispStr(40,y,s,DISP_BGND_BLACK+DISP_FGND_WHITE);
OSMemPut( IntBuffer, IntBlkPtr );
OSTimeDlyHMSM(0,0,2,0); //等待2s
}
}
/*-------------------------------Hertask-------------------------------------*/
void HerTask(void *pdata)
{
#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr;
#endif
pdata=pdata;
for( ; ; )
{
PC_DispStr(10,++y,s3,DISP_BGND_BLACK+DISP_FGND_WHITE);
IntBlkPtr=OSMemGet( //請求內存塊
IntBuffer, //內存分區的指針
&err); //錯誤信息
OSMemQuery( //查詢內存控制塊信息
IntBuffer, //帶查詢內存控制塊指針
&MemInfo);
sprintf(s,"%0x",MemInfo.OSFreeList); //顯示頭指針
PC_DispStr(30,y,s,DISP_BGND_BLACK+DISP_FGND_WHITE);
sprintf(s,"%d",MemInfo.OSNUsed); //顯示已用的內存塊數目
PC_DispStr(40,y,s,DISP_BGND_BLACK+DISP_FGND_WHITE);
OSMemPut(
IntBuffer,
IntBlkPtr
);
OSTimeDlyHMSM(0,0,1,0); //等待2s
}
}
/*
通過本例的實驗現象能深刻理解內存管理函數的本質;現象分析如下:
y(行) 顯示內容: 顯示頭指針OSFreeList: 顯示已用的內存塊數目OSNUsed:
1, MyTask 504 1
2, YouTask 50A 2
3, HerTask 50A 2
4, MyTask 50A 2
5, HerTask 510 3
6, MyTask 510 3
7, YouTask 516 4
8, HerTask 516 4
9, MyTask 516 4
10, HerTask 51C 5
11, MyTask 51C 5
12, YouTask 522 6
13, HerTask 522 6
14, MyTask 522 6
15, MyTask 522 6
通過上表可以看出:1,“顯示頭指針OSFreeList”的數據很有規律,即都是以6為單位遞增,這是因為在創建內存控制塊時就定義了每個內存塊的長度是6個字節,所以就是以6為單位遞增,(0x504+0x6)==0x50A;
(0x50A+0x6)==0x510,(0x510+0x6)==0x516,等等; 2,第2行中,因為YouTask任務請求一個內存塊之后就立即釋放了,所以在第3個任務(HerTask任務)中,HerTask任務申請到的還是50A指針指向的內存塊,同樣在本任務中及時釋放了內存塊,實際上此時系統只占用504內存塊,所以到第4個任務MyTask 任務執行時,還是顯示已用2個內存塊,不過這時MyTask 任務已經占用了兩個內存塊,因為此時MyTask 任務還沒有釋放內存塊;3,當if(Times>4) 時 (0-1-2-3-4-5;Times==6時該條件語句為真)MyTask 任務得到一個內存塊就立即釋放了該內存塊,所以就一直會顯示已用6個內存塊,指針值停留在0x522處,不會再遞增了;4,由此現象得知:a),本例中申請到的8個內存塊是連續的地址空間;b),系統在初始化時,就初始化了一個空的任務控制塊鏈表,每個任務控制塊的有效數據為空,但是結點指針都有所指;當系統申請一個內存分區時,空閑任務控制塊就會減1,釋放了就加1;c),整個空閑鏈表是不是一個大的連續內存空間,還不知道,我覺得應該看OSInit()源碼。
*/
|