自己實現一個RTOS《實時操作系統揭秘》(附源碼)


[非原創,轉載自CSDN論壇上的大神,原文鏈接http://bbs.csdn.net/topics/300000723

新年伊始, 
將自己獨立實現的一個比較小的RTOS源碼貼上來, 
順便把原理都講一講,希望對在這塊工作的朋友有些幫助或者啟發 
大家也給點改進的意見和建議。 

本系列文章的標題叫做《實時操作系統揭秘》 

第一篇 前言 
很多人對老美發明的操作系統頂禮膜拜,捧到了神的地步, 
市面上也充斥着很多有關操作系統的劣質的譯作 
對其關鍵部分,大部都語焉不詳,隔靴搔癢 
讓更多的人越看越糊塗 
於是操作系統在人們心中更加高深了 

其實,操作系統遠沒有這些人想象的那么神秘 
任務切換,內存管理,文件系統,任務間通訊功能,引導程序等模塊, 
就形成了一個完整的操作系統內核 
我們在這里就逐一剝一下操作系統的皮, 
把各個模塊的原理,以結合代碼的形式,抖給大家看看 
讓大家看清操作系統的一些秘密 
對某些模塊,系統功能有興趣的同學, 
也可以發郵件給我,unix.lxx@gmail.com 
我們一起研究,共同學習... 

(注: 
1,這系列文章, 
都是按照目前工作中,手頭項目的進度 
以及涉及到的知識點所寫出來的, 
是個類筆記的東西,是業余時間的一個作品 

2,附注的源碼, 
是個人的一個小作品, 
自己給他取了個不響亮的名字:BenOS 
並且該系統在自己的Cortex-M3平台測試通過並且運行自己的應用沒有問題 

3,BenOS是完全個人獨立實現的
沒有抄襲任何其他OS的源碼

 

文檔與源碼:   BenOS實時操作系統解密.rar     BenOS.src.rar

 

 

第二篇 任務切換,如何才能得到MM的青睞 

操作系統最核心的功能就是任務切換 

何為任務切換,為啥要任務切換 
這個就簡單說: 
引入操作系統,就是為了讓單個CPU可以運行多個任務 
但是這些任務是並發運行的, 
每個小豬吃一口奶,就讓給另一個小豬吃一口 
大家輪流來,宏觀看起來就像所有的小豬都在同時吃奶一樣 

任務A正在運行,時間該任務B運行了, 
這個時候,操作系統就需要做任務切換 
於是,操作系統就保存 任務A目前跑到了哪一步,運行時候,所有的參數是啥子 
然后在任務B恢復上次運行到的位置,並且恢復上次停止運行時間點的所有參數 
再打任務B一鞭子,任務B就像沒停止過一樣,開始歡快的跑了。 

這些,就是任務切換所需的全部步驟 
針對目前我們使用的CORTEX-M3平台而言 
任務切換就是, 
首先 
1,計算當前就緒態任務列表中,優先級最高的任務, 
  再判斷是否需要切換任務 
2,保存當前任務運行相關的寄存器,大概十幾個(入棧) 
  保存當前任務運行地址(PC指針,其實在1里面保存了) 
  保存當前的堆棧指針 
3,遞減各個任務等待的時間片計數器; 
4,其他資源占用情況統計(死鎖的解除) 
5,然后將堆棧指針指向新任務的堆棧 
6,恢復2中保存的所有寄存器和其他數據(出棧) 
7,其他系統功能相關的東西 
再給出例程代碼: 
        PUSH {R0-R15} ;(2) 
        LDR    R4, =OSTCBCur          ; OSTCBCur->OSTCBStkPtr = SP; 
        LDR    R4, [R4] 
        STR    SP, [R4]                ; 

        LDR    R6, =OSTCBHighRdy  ; SP=OSTCBHighRdy->OSTCBStkPtr 
        LDR    SP, [R6] 

        POP {R0-R15}            ; (6) 
        BX      LR                      ; RET,此時堆棧恢復了,寄存器恢復了,PC指針也恢復了, 
                                        ; 狀態也恢復了,舊任務就可以繼續歡樂的執行了。 

注:這里的堆棧指針都是另外賦值操作的, 
    絕對不能使用出棧入棧的方式保存恢復 
    因為出棧入棧操作會自動修改堆棧指針... 

這里主要是(2),(6)的代碼,並且沒有考慮模式切換和堆棧指針切換, 
其他步驟,一般實現都是在這切換之前進行。 

任務切換在很多時候都會進行,常說的任務切換就是在時間定時中斷時候進行, 
芯片(或者由外部時鍾)會產生一個定時中斷,可以設置每秒多少次, 
就是在這個中斷處理函數中進行任務切換。 
其他的任務切換,發生在,等待消息,任務延時,或者手動強制任務切換等等時候 
這些不同的切換時機都有些小差別,不過本質上是一樣的。 

夠簡單吧? 
其實實際上還有一些幕后工作並沒有列出, 
比如,內核要維護兩個鏈表,其一是就緒態任務的表,其二是等待態任務的表 
每個TICK中斷的時候,等待態表中的每個等待TICKS計數器就減一, 
當計數為0時,將其任務移動到就緒態表中來。 
還是有其他一些繁瑣的工作都要做,任務切換鎖定檢測,死鎖檢測,優先級反轉的避免等; 
並且死鎖和優先級反轉問題在實時系統中是個很深刻的課題,有興趣的不妨自己做一做。 
另外,指出一點:是依據時間片的操作系統,還是據優先級的搶占式操作系統, 
還是混合多種切換算法的操作系統的根本區別就是其切換算法, 
先進先出,后進先出,最短等待時間,最短平均等待時間,等等算法在這里都有用武之地! 

下面是自己的一個實現: 

/*
*時鍾中斷函數
*/
void SysTick_Handler()
{
        INT32        index;
        TCB                *pTCB;
        INT8U        flagFirstTask=0;
        INT_Stat  stat = 0;

        stat = BenOS_INT_Save();

        if (BenOSScheLock != 0)
        {
                BenOS_INT_Restore(stat);
                return;
        }

        /*在時鍾中斷中,必須將所有時延都--*/
        for (index = 0;index < TaskNUM;index++)
        {
                pTCB = BenOSTCBTable+index;
                /*該任務在睡眠狀態,而當前的調用是在時鍾中斷中*/
                if (pTCB->TCBDelay > 0)
                {
                        pTCB->TCBDelay--;
                }
                else
                {
                        if (flagFirstTask==0)
                        {
                                BenOSNewTCB = pTCB;
                                flagFirstTask = 1;
                        }
                }
                
        }

        if (BenOSNewTCB != BenOSCurTCB)
        {
                OSIntCtxSw();
        }
        BenOS_INT_Restore(stat);
}

/*
*在非中斷中 調度新的任務 並且切換
*/
void TaskSche()
{
        INT_Stat  stat = 0;

        stat = BenOS_INT_Save();
        if (BenOSScheLock != 0)
        {
                BenOS_INT_Restore(stat);
                return;
        }                

        if (BenOSNewTCB != BenOSCurTCB)
        {
                OSIntCtxSw();
        }
        BenOS_INT_Restore(stat);
}

__asm void OSCtxSw() 

        LDR    R4, =NVIC_INT_CTRL    
        LDR    R5, =NVIC_PENDSVSET 
        STR    R5, [R4]          ;激活PENDSVC中斷,開始切換任務 
        BX      LR 
        NOP 



__asm void PendSV_Handler() 

        MRS    R0, PSP                ; PSP is process stack pointer 

        SUB    R0, R0, #0x20          ; save remaining regs r4-11 on process stack 
        STM    R0, {R4-R11} 

        LDR    R4, =BenOSCurTCB        ; OSTCBCur->OSTCBStkPtr = SP; 
        LDR    R4, [R4] 
        STR    R0, [R4]                ; R0 is SP of process being switched out 

        LDR    R4, =BenOSCurTCB        ; BenOSCurTCB  = BenOSNewTCB; 
        LDR    R6, =BenOSNewTCB 
        LDR    R6, [R6] 
        STR    R6, [R4] 

        LDR    R0, [R6]                ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr; 
        LDM    R0, {R4-R11}            ; restore r4-11 from new process stack 
        ADD    R0, R0, #0x20 
        MSR    PSP, R0                ; load PSP with new process SP 
        ORR    LR, LR, #0x04          ; ensure exception return uses process stack 
        BX      LR                      ; exception return will restore remaining context 
        NOP 


事實上,這里只是實現的一部分,在后面展示的源碼中,會有其他的部分。
 
 
 

 

 

 
第三篇 世界的起源 任務如何啟動 

其實絕大部分操作系統的實現步驟都是這樣的: 
先壓點合適的東西進堆棧----堆棧的初始化 
(這步放在任務初始化的時候----切記關任務切換和定時中斷) 
只要恢復寄存器和其他數據之后 
SP指針正確,狀態寄存器沒異常, 
直接將PC指向新任務的第一條指令,不就行了嘛。 
看下我們CORTEX-M3平台上的實現:(堆棧生長方向是--,跟X86相反) 
/*把堆棧初始化模塊用匯編寫一遍,但願性能會高點*/ 
__asm    STACK_TYPE *TaskStkInit (void (*task),STACK_TYPE *ptos) 

        PUSH    {R4-R6,LR} 
        MOV      R4,R0 

        MOV      R0,R1 
        MOV      R5,#0x1000000 
        STR      R5,[R0,#0] 

        SUBS    R0,R0,#4 
        STR      R4,[R0,#0] 
        
        MVN      R5,#1 
        SUBS    R0,R0,#4 
        STR      R5,[R0,#0] 
        
        MOV      R5,#0x2 
        SUBS    R0,R0,#4 
        STR      R5,[R0,#0] 
        
        MOV      R5,#0x3 
        SUBS    R0,R0,#4 
        STR      R5,[R0,#0] 
        
        MOV      R5,#0x4 
        SUBS    R0,R0,#4 
        STR      R5,[R0,#0] 

        ASRS    R5,R5,#1 
        SUBS    R0,R0,#4 
        STR      R5,[R0,#0] 
        
        SUBS    R0,R0,#4 
        STR      R1,[R0,#0] 
        
        MOV      R5,#0x5 
        SUBS    R0,R0,#4 
        STR      R5,[R0,#0] 

        MOV      R5,#0x6 
        SUBS    R6,R0,#4 
        MOV      R0,R6 
        STR      R5,[R6,#0] 
        
        MOV      R5,#0x7 
        SUBS    R0,R0,#4 
        STR      R5,[R0,#0] 
        
        MOV      R5,#0x8 
        SUBS    R0,R0,#4 
        STR      R5,[R0,#0] 

        MOV      R5,#0x8 
        SUBS    R0,R0,#4 
        STR      R5,[R0,#0] 

        MOV      R5,#0x10 
        SUBS    R0,R0,#4 
        STR      R5,[R0,#0] 

        MOV      R5,#0x11 
        SUBS    R0,R0,#4 
        STR      R5,[R0,#0] 

        MOV      R5,#0x12 
        SUBS    R6,R0,#4 
        MOV   R0,R6 
        STR      R5,[R6,#0] 
        POP      {R4-R6,PC} 

}啟動這個任務,就可以直接使用任務切換源碼的后半部分(因為沒有任務需要保存) 
這樣PC就指向了新任務的入口, 
新任務可以開始運行啦! 
世界就這樣形成了... 

實際啟動是使用SVC中斷啟動 
代碼如下: 

__asm void StartTask() 

        LDR    R4, =NVIC_SYSPRI2      ; set the PendSV exception priority 
        LDR    R5, =NVIC_PENDSV_PRI 
        STR    R5, [R4] 

        MOV    R4, #0                  ; set the PSP to 0 for initial context switch call 
        MSR    PSP, R4 

        LDR    R4, =SYS_TICKS  ;設置時鍾節拍頻率    
        LDR    R5, =NVIC_SYSTICK_LOAD 
        STR    R4, [R5] 

        MOV    R4, #0x07      
        LDR    R5, =NVIC_SYSTICK_CTRL 
        STR    R4, [R5] 

      SVC 0    ;呼叫SVC中斷 
      NOP 


/*SVC中斷入口*/ 
__asm void SVC_Handler() 

      LDR R6,=BenOSCurTCB 
        LDR    R0, [R6]                ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr; 
        LDR    R0, [R0]                ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr; 

        LDM    R0, {R4-R11}            ; restore r4-11 from new process stack 
        ADD    R0, R0, #0x20 
        MSR    PSP, R0                ; load PSP with new process SP 
        ORR    LR, LR, #0x04          ; ensure exception return uses process stack 
        BX      LR                      ; exception return will restore remaining context 
}
 
 
 

 

 

 
第四篇 任務延時的實現 
這次,我們開始關心一個具體的系統接口的實現 
OSTimeDly (INT16U ticks) 

其實任務延時的實現已經在前面節拍中斷中有所體現 
在任務運行的過程中,常常需要任務等待很短的時間,再干活 
比如 
while(1 ) 

  msg=GetMsg(); 
  if (!msg) 
     OSTimeDly(10);/*等待10個時鍾*/ 
  else 
  { 
    dosomething(); 
    OSTimeDly(3);/*等待3個時鍾*/ 
  } 

那么,任務如何知道自己該等待了? 
如何知道自己等待了多久? 
如何繼續運行? 
sleepMS()又隱藏了哪些秘密呢? 

實現這些東西的原理是這樣的 
在每個任務TCB結構中, 
都存在一個OSTCBDlyTicks元素 
這個元素在每次定時中斷切換的時候就減一 
當減到0時,這個任務就可以繼續運行了 
具體的步驟如下: 
1,OSTCBDlyTicks賦值為該等待的時鍾次數 
2,將該任務從就緒表中刪除 
3,將該任務添加到等待表中 
4,在每次定時中斷時,將所有等待表中任務的OSTCBDlyTicks減一, 
  這樣每個任務都知道自己還將等待多久 
5,當某個任務的OSTCBDlyTicks減到了0,就將其在等待表中刪除,添加到就緒表 
這樣,這個任務進入到了可運行的列表里面,時機到了,就會運行。 
sleepMS的實現,其實很簡單: 
每個時鍾中斷的時間是固定的 
計算就能得出sleepMS該等待的ticks次數啦 
主要代碼如下: 
sleepMS(int ms) 

    ticks=ms*OS_TICKS_PER_SEC/1000; 
    OSTimeDly(ticks); 


源碼如下: 

/*目前設計是最大等待次數為255個時鍾片*/
void  BenOSTimeDly (INT32 ticks)
{
        INT_Stat  stat = 0;
        INT32          index;
        TCB                  *pTCB;
        
        stat = BenOS_INT_Save();
        BenOSCurTCB->TCBDelay = ticks;

        /*從當前任務向后遍歷,第一最大的優先級就是需要調度進去的任務*/
        for (index = 0/*(BenOSCurTCB- BenOSTCBTable)+1*/; index < TaskNUM;index++)
        {
                pTCB = BenOSTCBTable+index;
                if ((pTCB->TCBDelay == 0) && (pTCB->TaskStat !=BenOS_Task_Pend)          )
                {        
                        BenOSNewTCB = pTCB;

                        break;
                }
        }
        BenOS_INT_Restore(stat);
        TaskSche();        
}
 
 
 

 

 

 
第五篇,信號量的實現 
這里直接使用開關中斷作為臨界處理, 
不值得推薦,但是比抄襲別人的方案要好點,哈哈~ 

實現很直觀,代碼告訴你一切! 

#include "BenOSSemaphore.h"


/*當前信號量列表*/
SEMAPHORE_TYPE        BenOSSemaphore[MAX_SEM_NUM];                                
/*
* 創建信號量
*/
SEMAPHORE_TYPE*        CreateSemaphore(INT32 conuter)
{
        INT_Stat  stat = 0;
        INT32U         index;

        if (conuter < 0)
        {
                return 0;
        }
        
        stat = BenOS_INT_Save();
        for(index=0;index<MAX_SEM_NUM;index++)
        {        
                if (BenOSSemaphore[index]==-1)
                {
                        BenOSSemaphore[index] = conuter;
                        BenOS_INT_Restore(stat);
                        return &(BenOSSemaphore[index]);
                }
        }
        
        BenOS_INT_Restore(stat);
        return        (SEMAPHORE_TYPE*)NULL;
}

INT8 DeleteSemaphore(SEMAPHORE_TYPE* pSem)
{
        INT_Stat  stat = 0;

        stat = BenOS_INT_Save();

        /*信號量不存在*/
        if (pSem == NULL)
        {
                BenOS_INT_Restore(stat);
                return 0;
        }
        /*當且僅當信號量計數為0的時候,才能釋放該信號量*/
        if ((*pSem) != 0)
        {
                BenOS_INT_Restore(stat);
                return 0;
        }
        else
        {
                (*pSem) = (SEMAPHORE_TYPE)-1;
                BenOS_INT_Restore(stat);
                return 1;
        }
}


/*這個是一個不完全精確的實現*/
/*其超時時間不會非常精確*/
INT8 WaitSemaphore(SEMAPHORE_TYPE* pSem,INT32U timeout)
{
        INT32U         index;
        INT32U  stat = 0;
        for (index = 0;index < timeout;index++)
        {
                stat = BenOS_INT_Save();
                if ((*pSem) > 0)
                {
                        (*pSem)--;
                        BenOS_INT_Restore(stat);
                        return 1;/*獲取到了信號量*/
                }
                else
                {
                        /*等待一個時間片*/
                        BenOS_INT_Restore(stat);
                        BenOSTimeDly(1);
                }
        }
        
        return 0;
}

/*不等待,立即返回是否信號量能否獲取*/
INT8 GetSemaphore(SEMAPHORE_TYPE* pSem)
{
        INT32U  stat = 0;

        stat = BenOS_INT_Save();

        if ((*pSem) > 0)
        {
                (*pSem)--;
                BenOS_INT_Restore(stat);
                return 1;/*獲取到了信號量*/
        }
        BenOS_INT_Restore(stat);
        return 0;
}

/*釋放一個信號量*/
INT8 PostSemaphore(SEMAPHORE_TYPE* pSem)
{
        INT_Stat  stat = 0;

        stat = BenOS_INT_Save();
        (*pSem)++;
        BenOS_INT_Restore(stat);

        return 1;
}
 
 
 

 

 
第六篇 消息隊列的實現 

消息隊列是嵌入式編程中常用的工具, 
實現手段有很多,這里使用最直觀,最簡單的實現方式。 
UCOS和RTX都使用了信號量的隊列實現, 
我這里也直接使用中斷關閉的臨界區來實現 
(一般商用系統都不會使用這種方式, 
以后考慮使用Cortex-M3的一些特殊指令來重新實現,
包括信號量互斥量的實現也在考慮之列) 

#include "BenOSQueue.h"

/*用於對於的標記消息隊列是否使用,將來考慮用位來標志*/
INT8U        MsgQueueFlag[MAX_QUEUE_NUMBER]={0};

/*實際的所有消息隊列*/
EventQ        MsgQueue[MAX_QUEUE_NUMBER];

/*
* 創建消息隊列
*/
EventQ*        CreateQueue()
{
        INT_Stat  stat = 0;
        INT32U         index;
        
        stat = BenOS_INT_Save();
        for(index=0;index<MAX_QUEUE_NUMBER;index++)
        {        
                /*該消息隊列未被使用*/
                if (MsgQueueFlag[index]==0)
                {
                        /*該隊列首尾初始化*/
                        MsgQueue[index].front=0;
                        MsgQueue[index].rear=0;
                        BenOS_INT_Restore(stat);
                        return &(MsgQueue[index]);
                }
        }
        
        BenOS_INT_Restore(stat);
        return        (EventQ*)NULL;
}

INT8 DeleteQueue(EventQ* q)
{
        INT_Stat  stat = 0;
        
        stat = BenOS_INT_Save();

        /*信號量不存在*/
        if (q == NULL)
        {
                BenOS_INT_Restore(stat);
                return 0;
        }
        /*隊列指針越界*/
        if ((( q-MsgQueue ) < 0)||(( q-MsgQueue ) > (MAX_QUEUE_NUMBER-1)))
        {
                BenOS_INT_Restore(stat);
                return        0;
        }
        
        /*將標記位置0*/
        MsgQueueFlag[q-MsgQueue] =   (INT8U)0;

        BenOS_INT_Restore(stat);
        return        1;

}

/*發送一個消息*/
INT8 SendMessage(EventQ* q,MSG_TYPE msg)
{
        INT_Stat  stat = 0;
        
        stat = BenOS_INT_Save();
        /*隊列不存在*/
        if (q == NULL)
        {
                BenOS_INT_Restore(stat);
                return 0;
        }
        /*隊列指針越界*/
        if ((( q-MsgQueue ) < 0)||(( q-MsgQueue ) > (MAX_QUEUE_NUMBER-1)))
        {
                BenOS_INT_Restore(stat);
                return        0;
        }


        /*判斷隊列是否滿*/
        if((q->rear+1)%MAX_MSG_NUMBER==q->front)
        {        
                BenOS_INT_Restore(stat);
                return 0;
        }
        else
        {
                /*加入一個新的數據*/
                q->msgQueue[q->rear]=msg;
                /*將隊尾數加1*/
                q->rear=(q->rear+1)%MAX_MSG_NUMBER;
                BenOS_INT_Restore(stat);
                return 1;        
        }
}

MSG_TYPE WaitMessage(EventQ *q, INT32U timeout)
{
        INT32U         index;
        INT32U  stat = 0;
        MSG_TYPE msg;

        
        for (index = 0;index < timeout+1;index++)
        {
                stat = BenOS_INT_Save();

                if (q->front==q->rear)//判斷隊列是否為空
                {        
                        BenOS_INT_Restore(stat);
                        BenOSTimeDly(1);
                }
                else
                {
                        msg=q->msgQueue[q->front];//提出隊首數據
                        q->front=(q->front+1)%MAX_MSG_NUMBER;//改變隊首

                        BenOS_INT_Restore(stat);
                        return msg;
                }
        }

        BenOS_INT_Restore(stat);
        return -1        ;
}



MSG_TYPE GetMessage(EventQ *q, INT32U timeout)
{
        MSG_TYPE msg;
        INT32U  stat = 0;

        stat = BenOS_INT_Save();
        if (q->front==q->rear)//判斷隊列是否為空
        {        
                BenOS_INT_Restore(stat);
                return 0;        
        }
        else
        {
                msg=q->msgQueue[q->front];//提出隊首數據
                q->front=(q->front+1)%MAX_MSG_NUMBER;//改變隊首

                BenOS_INT_Restore(stat);
                return msg;
        }
}
 
 
 

 

 
第七章 實時性和優先級反轉
實時性和相關的優先級反轉問題,
在實時領域,是個很關鍵的問題

首先說多任務,
任務就是讓一段“流程”,一般都是一遍又一遍的循環運行(死循環)。
一次“流程”運行一遍之后,常常會等待一段時間,
自己休息休息,也讓其他任務也運行一下,
這就是多任務並行。
(大家都有房子住,這才是和諧社會嘛。)

在多任務的系統之中,實時性,就是讓當前最高優先級的任務優先運行;
若當前最高優先級的任務不是當前正在運行的任務,那么就要給一個時機(時鍾中斷),
讓高優先級的任務運行,正在運行的(低優先級)任務等下再運行。
這就是實時系統中的搶占調度。

實時操作系統的本質就是,
讓當前最高優先級的任務以最快的速度運行!
(如果有同優先級的任務,則大家輪流運行)

由此看來,實時的多任務設計,難度在於:
要保證系統性能滿足的需求,
在硬性保證高優先級任務在deadline之前運行完的同時
也要保證低優先級的任務順利的完成自己的工作。

當然,這里就提出了優先級反轉的問題了
典型情況如下:
高優先級的任務A要請求的資源被低優先級任務C所占用,
但是任務C的優先級比任務B的優先級低
於是任務B一直運行,比A低優先級的其他任務也一直能運行,
反而高優先級的任務A不能被運行了。

從實時性上講,若高優先級在等待一個某個資源,
那么為了保證高優先級任務能順利運行,
則必須要讓當前占用該資源的任務趕緊運行下去,直到把資源釋放。
再讓高優先級的任務來占用這個資源。

優先級反轉在RTOS中是一個很深刻的課題,
目前還沒有非常好的解決方案。
在這個問題上,目前業界比較典型的做法是VxWorks的做法
原理如下:
當任務A請求的資源被任務C所占用的時候
則將C的優先級提升到任務A的級別,讓占有資源的任務先運行,
這樣能在一定程度上解決優先級反轉的問題。
但是這樣做,事實上破壞了實時系統里面運行優先級的意義...

其他,有些商業RTOS也提出了一些解決方案
比如常見的極限優先級方案:
將使用資源的任務優先級提升到系統最高級別
使得任何使用該資源的任務都能快速通過
但是,對優先級意義的破壞性,比優先級繼承方案更大!

要是那位朋友有興趣和勇氣,提出了一個自己的好的解決方案,
那么在實時操作系統的發展史上,定會記錄下你的名字。


 
 
 

 

 
第八篇 總結 

事實上,這種基礎軟件方面的工作,在國內,都只做理論, 
或者說少有人關注,實踐的工作很少,基本上都是被國外的UCOS,FREERTOS,RTX,ucLinux概全了 
而這些又是軟件開發方面最核心的內容, 
我們不關心,那么我們國人的軟件開發永遠走在老美之后,。 
這個BenOS盡管不到商用水平,離工業標准很有距離, 
但是也算是一個嘗試,若不嘗試,永遠不知道味道, 
我的這個帖子是完全原創的作品,今天才拿出來放到網上 
希望大家能理性的看到這種實驗性質的OS 
當然,這個BenOS完全自己寫的,沒有抄襲任何其他OS的源碼。 

BenOS目前支持基於優先級搶占式的任務切換, 
我這邊測試的結論顯示:任務切換性能高於UCOS 
因為任務切換運算的所有運算都分布放在系統調用之中, 
(事實上,UCOS也是基於這種思想,但是沒BenOS徹底) 

內存使用量如下:(支持8個任務和8個信號量) 
      Code (inc. data)  RO Data    RW Data    ZI Data      Debug  Object Name 
      1100        32          0        32        192      4425  benoscore.o 
      204        36          0          0          0        244  benoscpu.o 
      268          6          0          0        32      4258  benossemaphore.o 
內核代碼約2K,OS的內存使用量小於300個字節
比較適合RAM 10K數量級的實時系統

以上不包括消息隊列,消息隊列是靜態分配,大小可以自己根據應用調整 
目前在 STM32f101RB 128K-rom 16K-ram 運行穩定。 
任務的結構如下: 
typedef struct taskControlBlock 


/*當前的棧頂指針*/ 
STACK_TYPE *pStackTop; 

/*當前優先級*/ 
PRIORITY_TYPE CurPriority; 
        
      /*任務狀態*/ 
INT8U TaskStat; 

/*等待時間片的個數*/ 
INT32 TCBDelay; 


} TCB, TASK_TYPE; 


BenOS支持信號量和消息隊列, 
互斥量只是信號量的一個特例(原理上講),所以沒有支持; 
優先級反轉問題的解決沒有實現,目前只能在應用中多考慮這點; 
動態內存分配的接口,沒有實現,目前業界有N多方案,可以直接拿來使用,跟平台無關 
這里主要是考慮到不管那種方案,即考慮實時性,又考慮內存碎片,都不會非常好。 


BenOS花了我上個月(08年12月份)所有的業余時間 
從第一調試到能啟動第一個任務,到后面的修修改改,到添加和測試各個模塊 
包括直接使用仿真器觀察 任務切換,堆棧使用情況,測試信號量和消息隊列 
另外還移植了公司的一個小的應用項目到平台之上。 
基本上運行穩定,在CPU高負荷的情況下,連續運行12個小時均正常(其中使用了信號量,消息隊列等) 

自己的測試結果: 
高優先級任務搶占效果明顯,比UCOS的搶占更好 
同樣的應用,6個任務同時運行(每個任務運行一圈,等待1個節拍), 
1,在UCOS上,6個任務均能運行, 
低優先級的任務每100個節拍才能運行1次, 
高優先級的任務越為一個節拍一次 

2,在BenOS上,6個任務均啟動了, 
但是只有4個任務能運行,高優先級的任務為一個節拍一次, 
而最低優先級的任務兩個任務根本搶占不到時鍾。


 
 
不懂就問, 不會就學。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM