小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

ucos在s3c2410上運行過程整體剖析-從main函數(shù)到UCOS初始化完畢

 phoenixcyan 2013-05-01

ucos在s3c2410上運行過程整體剖析-從main函數(shù)到UCOS初始化完畢

分類: μc /os ii 系統(tǒng)有關(guān)知識 416人閱讀 評論(0) 收藏 舉報
 

下面要講解的內(nèi)容基本上都是c語言編寫的了,還有一部分代碼是用匯編寫的。以下我就以這個系統(tǒng)移植的實驗源碼為例做詳細(xì)講解。講解的方式是根據(jù)程序執(zhí)行的順序進行,如果感覺有什么難理解的地方,會做一個宏觀講解和分析。

好了廢話不多說,進入main()函數(shù)之后做的事情有:

對硬件平臺做進一步的初始化操作

對ucos操作系統(tǒng)做初始化操作

 

對硬件的初始化主要包括:

初始化時鐘總線

初始化串口

初始化中斷結(jié)構(gòu)體的相關(guān)內(nèi)容

初始化定時器

 

關(guān)于以上的時鐘的,串口的,還有定時器的等硬件的具體初始化和使用我不想一一詳細(xì)闡述,這些你都可以通過查看datasheet自己弄明白的。畢竟我想詳細(xì)講解的是UCOS這個操作系統(tǒng),只不過有些硬件知識是不得不講的。我這里只是做一個你查資料的一個索引。

關(guān)于時鐘頻率初始化的相關(guān)知識:一,你可以查看datasheet的第七章CLOCK & POWER MANAGEMENT的內(nèi)容,也可以參考S3C-2410--2440完全開發(fā)流程的第十二個實驗的相關(guān)知識和內(nèi)容。

關(guān)于串口的你查看datasheet的第十一章uart的相關(guān)內(nèi)容。

關(guān)于定時器的你查看datasheet的第十章PWM Timer。

 

關(guān)于中斷結(jié)構(gòu)體,這是ucos響應(yīng)中斷的統(tǒng)一接口。就是定義了一個結(jié)構(gòu)體,里面包含了中斷服務(wù)函數(shù)的指針,屏蔽和開啟相應(yīng)中斷請求的函數(shù)指針等。整個中斷的詳細(xì)機制,我想在ARM平臺的中斷機制和ucos如何掛接的時候詳細(xì)講解。

現(xiàn)把main()函數(shù)的代碼貼出來:

int main(void)

{

 

       ARMTargetInit();        // do target (uHAL based ARM system) initialisation //

//初始化硬件平臺

       OSInit();               // needed by uC/OS-II //

//對ucos進行初始化

       OSTaskCreate(Task1,  (void *)0,  (OS_STK *)&task1_Stack[STACKSIZE-1],  Task1_Prio);

       OSTaskCreate(Task2,  (void *)0,  (OS_STK *)&task2_Stack[STACKSIZE-1],  Task2_Prio);

       OSAddTask_Init(0);

       OSStart();              // start the OS //

       // never reached //

       return 0;

}//main

 

 

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void Task1(void *Id)

{

      

 

       for(;;){

              printf("run task1\n");

              OSTimeDly(1000);

       }

}

void Task2(void *Id)

{

      

 

       for(;;){

              printf("run task2\n");

              OSTimeDly(3000);

       }

}

 

 

關(guān)于初始化硬件平臺的部分前面已經(jīng)說明了查看哪些資料,就不多說了。

講解關(guān)于ucos的初始化前,我要說一下這個ucos操作系統(tǒng)的相關(guān)知識,以及我講解這個操作系統(tǒng)的基本方法。

我們都知道操作系統(tǒng)的目標(biāo)就是充分利用計算機提供的硬件資源,合理進行調(diào)度并為用戶提供友好的借口。這些資源包括處理器(cpu),內(nèi)存,外圍設(shè)備 等,而其中ucos是一個嵌入式的實時操作系統(tǒng),它主要管理和調(diào)度的資源就是cpu。我們將剖析它是怎么實現(xiàn)cpu的較高的利用率的,以及是如何實現(xiàn)實時 性的。分析一下我們平時用電腦那種既能聽歌又能同時打游戲這樣的功能是怎么做到的。分析我們平時看到的現(xiàn)象和真實的實際情況有什么不同點。嘻嘻

我們都知道cpu的核心是一個叫晶振的東西,它能產(chǎn)生固有的脈沖頻率,cpu就在這種信號下有規(guī)律的工作,那操作系統(tǒng)是誰給提供這種時序性那,答案 是定時器,定時器以固定的時間向cpu發(fā)出中斷請求,而ucos的一些功能都是在定時器的中斷服務(wù)程序里實現(xiàn)的。所以定時器是操作系統(tǒng)必須的硬件,有了定 時器操作系統(tǒng)才有了生命。才能實現(xiàn)實時性!??!

因此,我將通過兩條線路來講解ucos操作系統(tǒng)的執(zhí)行流程:

一:正常的執(zhí)行流程

二:被定時器中斷后的中斷服務(wù)程序

需要注意的是這兩條線路是交叉執(zhí)行的,因為定時器中斷是周期性的,所以不論正常程序執(zhí)行到什么地方定時器中斷都有可能發(fā)生。其實,就是因為這種打斷性才使操作系統(tǒng)的多任務(wù)調(diào)度有了基礎(chǔ)。這個當(dāng)你充分從整體的角度理解了ucos操作系統(tǒng),你就知道了。

 

前面說了,ucos主要管理的是處理機的調(diào)度,那調(diào)度什么那?其實一個程序可能因為某些原因不能一直占有處理機,比如說他在等待一個資源,它再占用 處理機那就相當(dāng)于占著茅坑不拉屎。所以在有操作系統(tǒng)的時候,就可以有多個程序在不同的時間里占用cpu,這樣有執(zhí)行環(huán)境的程序,我們給他取個名字叫任務(wù)或 者進程。而ucos就是決定什么時候該那個任務(wù)運行的。

所以,在分析源碼之前還是要說明一個重要的概念,那就是任務(wù)(進程)。

相信大家對計算機能同時干這么多活這一現(xiàn)象可能存在疑問,那是什么機制讓我們的電腦做到了真正的三心二意那,那當(dāng)然屬操作系統(tǒng)了。它管理著我們的電 腦到底該干什么工作了?上一個工作干到什么程度了。我們也知道程序是一段有順序的解決問題的指令。我們的機器里面只有一個CPU,但可能有很多解決問題的 程序,他們肯定要爭奪CPU這個資源,怎么管理他們,讓他們有條不紊的運行下去那。這是我們今天要討論的問題,怎么樣,你有什么好的想法嗎?

其實,這個問題也很簡單,比如咱們現(xiàn)在先不說什么程序代碼。我們先說一個類似的問題。

讓你帶著5個小孩玩,你的任務(wù)就是看護這5個小孩,讓他們不打架,不受傷害,玩的開心。

而你現(xiàn)在只有一個游戲機,而你現(xiàn)在就面臨著這樣一種情況,怎么利用這一個游戲機讓所有的孩子都能玩好又不產(chǎn)生矛盾。

要是我解決這個問題的話,我可能會這樣去做,跟孩子們說:因為只有一個游戲機,你們5個只能輪流著玩這一個游戲機,沒人玩半小時,那至于誰先玩的問 題,我們這樣,今天誰先來的這里誰就先玩(即誰今天來的早誰就先玩)。好了,這個問題到現(xiàn)在為止解決了。不,到真正實施的時候可能會遇到一些問題,比如一 個孩子玩夠了半個小時,是讓他自己自愿提出來我玩的時間到了還是有你在旁邊計時并強制讓其給另一個孩子玩。還有每個孩子玩的游戲進度都不一樣,你可能還要 保存每一個孩子玩的進度問題,因為每一個孩子都只關(guān)心自己玩的游戲而不關(guān)心別的事情。當(dāng)然,還有一些其他的問題,一個孩子特別不懂事,非要提前玩游戲機, 你還要考慮這個問題。當(dāng)然,在實施過程中還有很多問題,你能想一想還有其他什么問題可能在這個過程中遇到嗎?

呵呵,看似一件很簡單的事情,做起來還真有難度。其實很多事情都是這樣,在一件事情還沒有真正做之前做一些計劃和方案是很難的,因為你不知道會遇到什么臨時情況。但我們一般會做計劃,并且要考慮所有可能遇到的情況以及遇到這些情況的處理方案。

好了,閑言少敘,回歸到我們要處理很多程序爭奪一個CPU的問題。顯然,程序沒有小孩那么智能,它不能聽懂一些規(guī)則。因此,我們必須把這些規(guī)則具體化,即用程序代碼去實現(xiàn)這些規(guī)則。因為計算機只認(rèn)識程序。

好了,上面一直說程序,這個很好理解,也就是一個算法。那這個算法怎么才能體現(xiàn)出它的具體價值那,恩,這是一個事。比如,你寫了一個程序計算兩個 10以內(nèi)的兩個數(shù)的加法。那你的目的很明確就是輸入兩個10以內(nèi)的數(shù),而得到這兩個數(shù)的結(jié)果。你可以通過計算機的輸入設(shè)備來進行輸入,可以利用輸出設(shè)備來 進行輸出結(jié)果。而程序在計算機運行一遍的這個過程所產(chǎn)生的效果是我們想要的。那把程序中的代碼一一運行產(chǎn)生效果的這個過程,原來那一段靜止的代碼就一步步 動起來了。當(dāng)然它運行起來了肯定會需要一些環(huán)境,這些環(huán)境當(dāng)然是計算機硬件提供的了,關(guān)于程序運行到底需要哪些具體的環(huán)境,我們下面具體探討。那我們給這 個正在在計算機內(nèi)部執(zhí)行的這個程序起個名字。叫什么那?

別人已經(jīng)起好了,叫進程了,或者任務(wù)。

現(xiàn)在大家明白什么叫進程了吧,其實就是一段代碼加上這段代碼在計算機內(nèi)部運行時所需要的所有環(huán)境。不管現(xiàn)在它運行沒運行,一段程序加上可在計算機內(nèi)部運行的能力,我們給他起了個名字叫進程(可能大概的意思是正在運行的程序吧)。

這是我對進程(任務(wù))的理解。這是在計算機操作系統(tǒng)中對底層最基本的一個抽象。在上面說的帶小孩玩的例子中,每一個小孩就是一個進程(任務(wù))。我會在下一個章節(jié)中具體以UCOS為例,具體講解怎么高效的管理這些小家伙(進程或者任務(wù))。

好了,下面說一下進程中所要包含的具體硬件環(huán)境,咱們就以arm920t為實例,說明在這上面運行的程序到底需要哪些環(huán)境,他們這些叫進程的小家伙生活在什么環(huán)境下那?我想你一定很想知道的。

我們都知道的,arm920t有運行模式,我們就選其中一個模式來說。

R0~R12這些寄存器是他們生活的必需品,還有sp(r13)堆棧指針,lr(r14)the linker register ,還有程序計數(shù)器PC(r15),還有就是當(dāng)前程序運行狀態(tài)寄存器CPSR,以及SPSR。

這些寄存器就是程序執(zhí)行時的所有硬件環(huán)境。其實也很簡單,哈。

你想一想,如果你把一個程序在某一時刻打斷了,但是你把這些寄存器都保存到一個地方了,你下次再把這些內(nèi)容都恢復(fù)到各個寄存器里面,那這個程序就按照原來被打斷的地方開始執(zhí)行嘍。就相當(dāng)于把這個小生命給暫定了,靜止了,然后在一個合適的時候它又恢復(fù)了原來的生活。呵呵呵。

好,現(xiàn)在我們理解了什么是進程以及它生活的環(huán)境。那么只要我們能記住每個進程的狀態(tài),我們就可以管理他們了。你想管理他們那就要首先了解他們,并且 能記憶他們被打斷時的狀態(tài)。這樣我們就能恢復(fù)這個任務(wù)的所有狀態(tài)了。如果我們有很好的調(diào)度算法,那就可以很好的管理他們啦。另外,我們的操作系統(tǒng)還要記錄 進程的運行狀態(tài),在ucos里包括,運行態(tài),就緒態(tài),阻塞態(tài)等狀態(tài),這樣有利于管理這些進程。

下面說說操作系統(tǒng)一般是怎么記憶這些東西的,那就要定義一個數(shù)據(jù)結(jié)構(gòu)用來記憶這些信息。這個東西起個名字叫任務(wù)控制塊(TCB,task contorl block)。

我們看看這個ucos的任務(wù)控制塊的結(jié)構(gòu):

我們跳過那些宏條件編譯,只說明核心的部分。

typedef struct os_tcb {

    OS_STK          *OSTCBStkPtr;  /* Pointer to current top of stack */              

//這個是任務(wù)的堆棧的棧頂指針。我說一下,在ucos中每個任務(wù)都有自己的堆??臻g,用于保存程序運行中函數(shù)調(diào)用的情 況(這點可以從c語言執(zhí)行和堆棧這里知道棧的重要性)以及當(dāng)發(fā)生任務(wù)切換時保存任務(wù)執(zhí)行的狀態(tài)。也就是那些寄存器的內(nèi)容在發(fā)生任務(wù)切換時就都入到自己任務(wù) 的堆棧里去。

#if OS_TASK_CREATE_EXT_EN > 0

    void            *OSTCBExtPtr;   /* Pointer to user definable data for TCB extension */

    OS_STK          *OSTCBStkBottom; /* Pointer to bottom of stack*/                                  

    INT32U           OSTCBStkSize; /* Size of task stack (in number of stack elements) */            

    INT16U           OSTCBOpt;      /* Task options as passed by OSTaskCreateExt()*/                 

    INT16U           OSTCBId;       /* Task ID (0..65535) */                                         

#endif

 

    struct os_tcb   *OSTCBNext;  /* Pointer to next     TCB in the TCB list*/                     

    struct os_tcb   *OSTCBPrev;  /* Pointer to previous TCB in the TCB list*/                     

//這兩個是任務(wù)控制塊指針,最后這些任務(wù)的控制塊是鏈接成雙向鏈表的,以便于查找和修改任務(wù)控制塊。

#if OS_EVENT_EN

    OS_EVENT        *OSTCBEventPtr;    /* Pointer to event control block*/                              

#endif

 

#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)

    void            *OSTCBMsg; /* Message received from OSMboxPost() or OSQPost()*/             

#endif

 

#if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)

#if OS_TASK_DEL_EN > 0

    OS_FLAG_NODE    *OSTCBFlagNode;    /* Pointer to event flag node*/                                  

#endif   

    OS_FLAGS         OSTCBFlagsRdy;    /* Event flags that made task ready to run*/                      

#endif

 

INT16U           OSTCBDly;  /* Nbr ticks to delay task or, timeout waiting for event*/

//這個是延時的ticks數(shù)量,ticks是每秒定時器產(chǎn)生的中斷數(shù)量,一般是每秒1000個ticks。也就是每秒產(chǎn)生1000個定時器中斷。這個變量是用來記憶一個任務(wù)延時的ticks的,比如一個任務(wù)自己想延時1s鐘,那這個變量就要賦值1000.

INT8U            OSTCBStat;        /* Task status */  

//任務(wù)的狀態(tài),用于記憶任務(wù)是運行態(tài),就緒態(tài),還是……                                             

    BOOLEAN    OSTCBPendTO; /* Flag indicating PEND timed out (TRUE == timed out)*/

INT8U            OSTCBPrio;        /* Task priority (0 == highest, 63 == lowest) */

//這個是任務(wù)優(yōu)先級,這個ucos是根據(jù)優(yōu)先級進行調(diào)度的,優(yōu)先級是我們創(chuàng)建任務(wù)時賦給任務(wù)的一個優(yōu)先順序值,在 ucos中這個數(shù)值越小,優(yōu)先級越高,比如優(yōu)先級為3的任務(wù)和優(yōu)先級為8的任務(wù)都就緒,那ucos首先運行任務(wù)3.  關(guān)于ucos高效的任務(wù)調(diào)度算法我們將在以下文章中詳細(xì)講解。            

    INT8U         OSTCBX; /* Bit position in group  corresponding to task priority (0..7) */

    INT8U            OSTCBY;/* Index into ready table corresponding to task priority */     

    INT8U            OSTCBBitX;      /* Bit mask to access bit position in ready table */  

    INT8U            OSTCBBitY;/* Bit mask to access bit position in ready group*/ 

//上述四個變量是和高效率調(diào)度任務(wù)有關(guān)的。

#if OS_TASK_DEL_EN > 0

    INT8U            OSTCBDelReq;/* Indicates whether a task needs to delete itself*/ 

#endif

 

#if OS_TASK_PROFILE_EN > 0

    INT32U       OSTCBCtxSwCtr;    /* Number of time the task was switched in */       

    INT32U  OSTCBCyclesTot;   /* Total number of clock cycles the task has been running*/   

    INT32U   OSTCBCyclesStart; /* Snapshot of cycle counter at start of task resumption*/      

    OS_STK  *OSTCBStkBase;     /* Pointer to the beginning of the task stack */          

    INT32U    OSTCBStkUsed;     /* Number of bytes used from the stack */

#endif

#if OS_TASK_NAME_SIZE > 1

    char             OSTCBTaskName[OS_TASK_NAME_SIZE];

#endif

} OS_TCB;

關(guān)于這個任務(wù)控制塊,這里只是做一下簡單的說明,如果有什么不理解的地方?jīng)]有關(guān)系,只要知道定義這個TCB結(jié)構(gòu)體的目的就行。我們會在下面的程序分析中詳細(xì)地說明的。

前面老是說任務(wù)切換和任務(wù)調(diào)度,我說一下這兩個概念的區(qū)別,任務(wù)調(diào)度就是決定要執(zhí)行那個任務(wù)。比如現(xiàn)在有優(yōu)先級為2,15,34的任務(wù)就緒,那任務(wù) 調(diào)度要找出優(yōu)先級最高的任務(wù),即任務(wù)2,這個任務(wù)將要運行。而任務(wù)切換就是在合適的時間里將當(dāng)前正在運行的任務(wù)保存進度,轉(zhuǎn)而去執(zhí)行調(diào)度程序得到的最高優(yōu) 先級的任務(wù)2。這也就是切換的真正含義。

 

下面我們到OSInit()這個函數(shù)內(nèi)部去看看它具體怎么初始化的,

下面貼代碼:

void  OSInit (void)

{

#if OS_VERSION >= 204

    OSInitHookBegin();  /* Call port specific initialization code   */

#endif

    OS_InitMisc();  /* Initialize miscellaneous variables       */

 

    OS_InitRdyList();   /* Initialize the Ready List                */

 

    OS_InitTCBList();      /* Initialize the free list of OS_TCBs      */

 

OS_InitEventList();    /* Initialize the free list of OS_EVENTs    */

 

……………………

  OS_InitTaskIdle(); 

 

看代碼就知道這個文件實現(xiàn)了對全局雜項變量的初始化,就緒表的初始化,任務(wù)控制塊的初始化,初始化空閑任務(wù)等工作,當(dāng)然還有其他的初始化操作,這些 和我們的操作系統(tǒng)核心內(nèi)容關(guān)系就不是很多了,我們盡量精簡這個ucos,只展現(xiàn)其核心內(nèi)容,讓大家明白ucos操作系統(tǒng)是怎么實現(xiàn)對處理機調(diào)度的。即多任 務(wù)實時操作系統(tǒng)到底是怎么實現(xiàn)的。

 

所謂ucos的初始化就是對那些核心的全局變量進行清零工作,使其處于可知的狀態(tài)。

OSIntNesting  = 0;           /* Clear the interrupt nesting counter      */

//這個全局變量是用于記錄中斷嵌套層數(shù)的

OSLockNesting = 0;       /* Clear the scheduling lock counter          */

//這個全局變量是給任務(wù)上鎖的標(biāo)志

OSTaskCtr     = 0;       /* Clear the number of tasks                */

//這個變量用于記錄系統(tǒng)中的任務(wù)數(shù)量

OSRunning     = FALSE;   /* Indicate that multitasking not started     */

//這個變量用于標(biāo)志ucos的運行狀態(tài)的

OSCtxSwCtr    = 0;       /* Clear the context switch counter         */

//這個變量用于記錄系統(tǒng)進行任務(wù)切換的次數(shù)的

OSIdleCtr     = 0L;       /* Clear the 32-bit idle counter            */

//這個是空閑任務(wù)用于計數(shù)的變量??臻e任務(wù)是這樣一種任務(wù),在沒有任何可執(zhí)行的用戶任務(wù)的時候,就要執(zhí)行空閑任務(wù),它什么也不干,只是讓 OSIdleCtr這個變量加一。因為cpu在加電后就不能停止執(zhí)行指令,即是用戶沒有需要執(zhí)行的任務(wù),那也要執(zhí)行一個空閑任務(wù),只要cpu是按照我們的 意愿去執(zhí)行程序,那什么就都在我們的掌握之中,是吧。

下面開始初始化就緒表的相關(guān)變量。

 

static  void  OS_InitRdyList (void)

{

    INT8U    i;

    INT8U   *prdytbl;

 

    OSRdyGrp      = 0x00;       /* Clear the ready list                     */

    prdytbl       = &OSRdyTbl[0];

    for (i = 0; i < OS_RDY_TBL_SIZE; i++) {

        *prdytbl++ = 0x00;

    }

//上述代碼是把就緒表的相關(guān)變量清零。

    OSPrioCur     = 0;

//這個變量是當(dāng)前任務(wù)的優(yōu)先級

    OSPrioHighRdy = 0;

//這個是當(dāng)前最高的優(yōu)先級

    OSTCBHighRdy  = (OS_TCB *)0; 

//最高優(yōu)先級的任務(wù)控制塊                             

    OSTCBCur      = (OS_TCB *)0;

//當(dāng)前優(yōu)先級的任務(wù)控制塊

}

 

// 設(shè)計就緒表有兩個目的:一,記錄那些任務(wù)就緒。二,從這些就緒的任務(wù)中找出優(yōu)先級最高的。就緒表是和任務(wù)調(diào)度有關(guān)系的數(shù)據(jù)結(jié)構(gòu),關(guān)于它的詳細(xì)情況,在下面進行詳細(xì)講解。

static  void  OS_InitTCBList (void)

{

    INT8U    i;

    OS_TCB  *ptcb1;

    OS_TCB  *ptcb2;

 

 

OS_MemClr((INT8U *)&OSTCBTbl[0],sizeof(OSTCBTbl));/* Clear all the TCBs */ 

//對任務(wù)控制塊進行清零操作

OS_MemClr((INT8U *)&OSTCBPrioTbl[0], sizeof(OSTCBPrioTbl)); /* Clear the priority table */

// 對任務(wù)控制塊優(yōu)先級指針?biāo)饕M行清零操作

    ptcb1 = &OSTCBTbl[0];

    ptcb2 = &OSTCBTbl[1];

    for (i = 0; i < (OS_MAX_TASKS + OS_N_SYS_TASKS - 1); i++) {  /* Init. list of free TCBs            */

        ptcb1->OSTCBNext = ptcb2;

#if OS_TASK_NAME_SIZE > 1

        ptcb1->OSTCBTaskName[0] = '?'; /* Unknown name    */

        ptcb1->OSTCBTaskName[1] = OS_ASCII_NUL;

#endif

        ptcb1++;

        ptcb2++;

}

//以上代碼是把原來申請的TCB控制塊的數(shù)組,鏈接成一個單向鏈表,這樣有利于對TCB的操作。

ptcb1->OSTCBNext = (OS_TCB *)0;   /* Last OS_TCB */

//最后一個TCB的next指針指向空

#if OS_TASK_NAME_SIZE > 1

    ptcb1->OSTCBTaskName[0] = '?';  /* Unknown name */

    ptcb1->OSTCBTaskName[1] = OS_ASCII_NUL;

#endif

OSTCBList  = (OS_TCB *)0;     /* TCB lists initializations          */

// OSTCBList 指向空,OSTCBList是創(chuàng)建后任務(wù)的任務(wù)控制塊的頭

OSTCBFreeList           = &OSTCBTbl[0];

//空閑任務(wù)控制塊的鏈表頭

 

}

 

我們對ucos初始化就是對ucos中的全局變量進行清零操作。在我們平時所寫的程序里,可以動態(tài)分配內(nèi)存,那是有操作系統(tǒng)的支持。現(xiàn)在我們寫的是 操作系統(tǒng),因此不能用動態(tài)方式分配內(nèi)存,ucos是這么做的,分配一些全局變量,然后為了好管理這些全局變量,我們把這些變量做成鏈表。當(dāng)然我們申請的全 局變量是結(jié)構(gòu)體,結(jié)構(gòu)體里有指向下一個結(jié)構(gòu)體的指針,我們初始化只是把一些變量鏈接成鏈表的形式。

 

Ucos中對全局變量的初始化就這些,不好呀,突然出現(xiàn)這么多變量,都不知道他們是干什么的?他們之間有什么關(guān)系?這沒事,我下面對這些變量以及他們的作用,和他們以后的用途,還有他們之間的關(guān)系做一個詳細(xì)的說明。

主要分為以下幾個核心部分:

1,  關(guān)于TCB的來龍去脈,一開始的邏輯結(jié)構(gòu)以及到最后的邏輯結(jié)構(gòu)。怎么說這個TCB是整個ucos的核心數(shù)據(jù),用于控制任務(wù)的唯一數(shù)據(jù)。也是任務(wù)在ucos中存在的唯一標(biāo)示。

2,  關(guān)于就緒表和任務(wù)調(diào)度

3,  其他幾個核心變量的相關(guān)內(nèi)容。

 

有了以上對這些數(shù)據(jù)的邏輯結(jié)構(gòu)上的認(rèn)識以及他們的作用,再加上前面咱們講解的相關(guān)內(nèi)容,我想我們在閱讀源代碼時就有了一定的思路。按照正常路線和中斷路線兩條路線去理解。

 

我們對這個TCB應(yīng)該不陌生了,它是一個記錄任務(wù)的相關(guān)信息的一個結(jié)構(gòu)體。這個TCB空間一開始是程序通過數(shù)組的形式申請的全局變量存儲空間,然后 在ucos的初始化中做成一個單項鏈表,且它的鏈表頭為OSTCBFreeList,當(dāng)我們創(chuàng)建一個任務(wù)需要TCB時就從這個由TCB組成的線性鏈表上摘 除一個,然后進行TCB的初始化,最后創(chuàng)建的任務(wù)的TCB連接成一個雙向鏈表,且有一個以任務(wù)優(yōu)先級為索引的TCB指針,這樣設(shè)計數(shù)據(jù)結(jié)構(gòu)的目的是方便通 過優(yōu)先級進行查找相應(yīng)的TCB。

一開始,空閑TCB的狀態(tài)如圖:

 

當(dāng)創(chuàng)建了幾個任務(wù)之后TCB的狀態(tài)如下:

我們先不看創(chuàng)建的任務(wù)是什么,而且這個圖是從書上截取的,把這張圖放到這個地方的原因是讓我們知道OSTCBPrioTbl這個數(shù)組,以及TCB之 間是怎樣一種邏輯結(jié)構(gòu)。OSTCBPrioTbl這個數(shù)組是一個索引數(shù)組,用于優(yōu)先級的快速查找TCB。比如你知道一個優(yōu)先級為3,那 OSTCBPrioTbl[3]里的指針指向的就是優(yōu)先級為3的任務(wù)控制塊。那么再看這些TCB控制塊是什么邏輯結(jié)構(gòu),這些控制塊連接成雙向鏈表,這個雙 向鏈表的頭是OSTCBList。

對TCB 這個控制塊的整體結(jié)構(gòu)要有一個宏觀的了解,這樣我們看源碼時才不會一頭霧水,沒有頭緒。

關(guān)于就緒表,我要詳細(xì)的說明。

就緒表是用于記錄哪些任務(wù)是就緒狀態(tài)的,創(chuàng)建就緒表的目的有兩個:

1,  記錄哪些任務(wù)是就緒態(tài)

2,  通過就緒表查找優(yōu)先級最高的任務(wù)

 

首先,我們要先介紹一下OSRdyGrp和OSRdyTbl這兩個東西,OSRdyGrp這個是一個無符號的八位數(shù),OSRdyTbl是一個數(shù)組, 是由八個八位無符號數(shù)組成的一個數(shù)組。這兩個東東是干什么的那,前面說了,我們要知道系統(tǒng)中有哪些任務(wù)處于就緒態(tài),那就需要記憶,要記憶就需要存儲空間。 那具體要記憶哪些信息那,如果一個任務(wù)被創(chuàng)建,我們既需要知道這個任務(wù)是否處于就緒態(tài),還要知道這個就緒的任務(wù)優(yōu)先級是多少。Ucos支持64個優(yōu)先級, 我們可以定義一個數(shù)組xx[64]來記錄那個任務(wù)就緒了,比如優(yōu)先級為15的任務(wù)就緒了,我們可以把xx[15]置一表示優(yōu)先級為15的任務(wù)就緒了,置0 表示任務(wù)未就緒。當(dāng)我們有很多任務(wù)就緒時,這個數(shù)組中就有很多被置一,查找優(yōu)先級最高的(在ucos中就是數(shù)值最小的)任務(wù),那一種方法就是從數(shù)組的下標(biāo) 0開始掃描,當(dāng)?shù)谝淮斡龅?時的數(shù)組下標(biāo),這個優(yōu)先級的任務(wù)就是當(dāng)前最高的優(yōu)先級的任務(wù)。這也就實現(xiàn)了上述的兩個目的,當(dāng)然還有其他很多方法,我剛才想的 這個方法有兩個缺點,一是這樣有點浪費空間,二是這樣尋找最高優(yōu)先級的算法復(fù)雜度有點高,不太符合實時調(diào)度的要求。咱們看看ucos的作者Jean J.Labrosse是怎么解決的這個問題。首先他用的是位變量來表示任務(wù)就沒就緒,即用變量的每一位的數(shù)值是0還是1來表示任務(wù)就沒就緒的狀態(tài)。用某一 位置一說明這個任務(wù)就緒,置零說明這個任務(wù)未就緒。關(guān)鍵問題是怎么在記錄這兩種狀態(tài)的同時又能記錄這個優(yōu)先級的數(shù)值。使用這樣一個方案,申請有64個位的 數(shù)組,如果申請的變量是無符號8位的,那么我們就要申請xx[8],每個變量8位,數(shù)組共8個變量,因此有64個二進制位,用每一位的0和1表示就沒就 緒,同時被置一的位本身也就代表著數(shù)值的大小??纯聪旅娴膱D:

這些是64個位變量,中間的我省略了,然后那,從右邊往左邊編號(從0編到63),你比如說優(yōu)先級為15的任務(wù)就緒,你就可以把從左邊數(shù)(當(dāng)然從零 開始),數(shù)到第15個位時把這個位置一即可。這64個位的空間怎么來的那,是申請了一個包括8個無符號數(shù)的數(shù)組,其數(shù)組下表是0~7.在c語言中也就是把 這64個位空間,分為了8組,每組一個8位無符號變量,共8個變量。我們在c語言中對每一位置位時就可以先看這個優(yōu)先級按照這種方法應(yīng)該在第幾組里置位, 然后再確定在這個組里的具體位置。我們知道優(yōu)先級變量OSTCBPrio是一個無符號的8位數(shù)。咱們就以一個具體事例來說明怎么利用這個方法來實現(xiàn)對相應(yīng) 的位置一。比如現(xiàn)在有一個優(yōu)先級為12的任務(wù)就緒,把這個十進制的數(shù)值化為二進制為00001100,你把低三位看成是組里的第幾個數(shù)的話,那高三位就代 表這個優(yōu)先級的任務(wù)所在的組號。(其中低三位是紅色顏色標(biāo)注的,高三位是藍(lán)色顏色標(biāo)注的),那么我們就知道這個優(yōu)先級的任務(wù)屬于001(即第一組),在組 內(nèi)的位置是100(即第四個)。這樣我們就很容易用代碼實現(xiàn)對某一固定的位置一操作。至于代碼的真正實現(xiàn),我們到分析代碼時在具體分析。僅看到這 些,Jean J.Labrosse的實現(xiàn)方法也就是節(jié)約了內(nèi)存空間而已,但精華不止這些,還有另外一個很重要的問題就是怎么從這些就緒的任務(wù)中迅速找出優(yōu)先級最高的 來。上面介紹了利用OSRdyTbl這個數(shù)組如何實現(xiàn)記錄任務(wù)的優(yōu)先級和是否就緒的狀態(tài)?,F(xiàn)在來看一下OSRdyGrp這個變量,這個變量用來記憶那組里 有就緒的任務(wù)。比如第三組里有就緒的任務(wù),那這個變量的第三位的位變量就置一。這兩個變量的邏輯結(jié)構(gòu)如下:

那好,我們前面也已經(jīng)知道了我們用OSRdyTbl這個數(shù)組的每一位是0還是1來表示這個任務(wù)是否就緒。用這個1處于的位置(從左向右數(shù)的位變量的個數(shù))來表示這個優(yōu)先級的大小。

那怎么得到最高優(yōu)先級那(在ucos里是數(shù)值最小的),當(dāng)然是利用OSRdyTbl這個數(shù)組,前面說的是創(chuàng)建任務(wù)時對其進行置位操作,現(xiàn)在就利用已 經(jīng)置位的OSRdyTbl這個數(shù)組來找到最小的優(yōu)先級,方法很簡單,就是從左到右掃描當(dāng)遇到第一個1時的位變量的位置的數(shù)值就是當(dāng)前的最高優(yōu)先級。當(dāng)然我 們不是直接在OSRdyTbl數(shù)組里直接線性掃描的。先看OSRdyGrp這個變量,看看那個組里有就緒的任務(wù),同樣的方法遇到第一個一的位的位數(shù)即為最 高優(yōu)先級的任務(wù)所在的組號。所在的組號乘以8再加上在組里的位置,即得到當(dāng)前優(yōu)先級最高的任務(wù)的優(yōu)先級。但現(xiàn)在的問題是我怎么根據(jù)OSRdyGrp和 OSRdyTbl[]的值得到他們第一個1是第幾位哪。只有建立一個索引表,這個表以他們的數(shù)值為索引,以他們的值中從左邊數(shù)第一個1的位置為內(nèi)容。 即   

你比如說你的OSRdyGrp的值最后是00110110的第一個1的位置是1,第零個數(shù)是0,第一個數(shù)是1.

而根據(jù)這個表,數(shù)組下標(biāo)為0x46的內(nèi)容是1.

 

根據(jù)上面講的這些內(nèi)容,你就能實現(xiàn)記錄任務(wù)優(yōu)先級的值和狀態(tài),還有就是快速的實現(xiàn)查找就緒任務(wù)中優(yōu)先級最高的任務(wù)。

上面說的有點亂,要真正理解這個機制還需要看源碼或者是看《嵌入式實時操作系統(tǒng)ucos-ii》。下面對這個機制做一個總結(jié)。

我們的兩個目的:一,記錄優(yōu)先級及優(yōu)先級的狀態(tài)。二,在就緒的任務(wù)中實現(xiàn)快速查找優(yōu)先級最高的任務(wù)。

作者所用方法中的精華:

1,  利用位變量存儲任務(wù)狀態(tài),節(jié)約存儲空間。同時把優(yōu)先級的數(shù)值用申請的連續(xù)空間的位的位置來表示。

2,  在進行置位、清零操作時,利用分組的概念實現(xiàn)。因為64個連續(xù)的位空間是由8個8位的無符號數(shù)組成的,其下標(biāo)是0~7,然后任務(wù)的優(yōu)先級也可以看成是第幾組的第幾個。比如:00011100就可以看成是第三組的第四個優(yōu)先級。

3,  為了實現(xiàn)快速查找,作者又增加了一個變量OSRdyGrp,某一位置一表示這一組內(nèi)有就緒的任務(wù),否則沒有就緒任務(wù)。比如,OSRdyGrp的值為 00000011就表示第0組和第一組都有就緒任務(wù),在這種情況下肯定第0組的任務(wù)優(yōu)先級高。也就是說第一個遇到1的位的位置代表的是優(yōu)先級高的任務(wù)所在 的組。同樣的道理,OSRdyTbl[0]的值假設(shè)是00110010這個數(shù)第一個位置是1(都是從0開始數(shù))。那就代表第零組的第一個任務(wù)就緒了,而且 是優(yōu)先級最高的?,F(xiàn)在看來,最主要的問題是找第一個1,因為它的位置代表的優(yōu)先級最高(也即代表的數(shù)值最小,其他的數(shù)值肯定比這個數(shù)值大)。為了快速的知 道OSRdyGrp和OSRdyTbl[OSRdyGrp]第一個1在什么位置,我們建立了一個數(shù)組OSUnMapTbl[256],這個數(shù)組的下標(biāo)是無 符號8位變量的所有值,即OSRdyGrp或者OSRdyTbl[OSRdyGrp]的所有取值情況,而數(shù)組的內(nèi)容是這個數(shù)的第一個1的位的位置(從右邊 數(shù))。這樣根據(jù)OSRdyGrp和OSRdyTbl[OSRdyGrp]的值就直接可以通過查表OSUnMapTbl[256]和簡單的計算就能的到最高 優(yōu)先級的數(shù)值了。計算方法是通過OSRdyGrp這個索引查到的數(shù)值乘以8(因為每組八個)加上通過OSRdyTbl[OSRdyGrp]這個索引查表得 到的數(shù)。這個方法是利用了空間換取時間效率的高效算法,O(1)算法,算法復(fù)雜度不變,無論有多少任務(wù)就緒(當(dāng)然現(xiàn)在是64個)。

 

上面就是對ucos任務(wù)調(diào)度算法的一個分析。

 

下面說一下其他幾個全局變量:

OSTaskCtr記錄的是操作系統(tǒng)中創(chuàng)建任務(wù)的數(shù)量。

OSCtxSwCtr是操作系統(tǒng)任務(wù)切換的次數(shù)

OSIdleCtr是當(dāng)空閑任務(wù)運行時這個變量持續(xù)加一。用這個變量可以計算cpu的利用率,具體方法可以參考《嵌入式實時操作系統(tǒng),ucos-ii》的第三章的統(tǒng)計任務(wù)有關(guān)章節(jié)。

OSIntNesting是記錄中斷嵌套層數(shù),進入一層中斷這個值就加一,推出中斷后就減一。

這樣,我們就對這個ucos操作系統(tǒng)有了一個宏觀的認(rèn)識,到現(xiàn)在為止,我們的ucos已經(jīng)初始化完畢,下一步我們就該創(chuàng)建任務(wù)了,當(dāng)創(chuàng)建完任務(wù)后,開啟ucos的多任務(wù)調(diào)度后,ucos就開始真正運行。下一部分就主要講解ucos創(chuàng)建任務(wù)到多任務(wù)開始調(diào)度運行的所有過程。

 

寫到后面突然發(fā)現(xiàn)一個很重要的問題沒有說明,那就是有關(guān)開中斷和關(guān)中斷的相關(guān)問題。

在ucos內(nèi)部,當(dāng)處理一些共享性的資源時我們要實現(xiàn)互斥訪問,即對這些共享資源要進行保護。比如說ucos對TCB進行操作時,你要保證你的操作 代碼要對TCB有完全的讀寫權(quán)限,還要保證只有你這一段代碼在操縱TCB。要實現(xiàn)對TCB的互斥訪問,以避免因競爭共享資源而產(chǎn)生的錯誤。要實現(xiàn)對共享資 源的互斥訪問,方法有很多,在ucos中主要使用的是關(guān)中斷的方法。在ucos定義了兩個宏; OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。

這兩段代碼都是用匯編書寫的,其實開關(guān)中斷很簡單,就是對CPSR的I、F位置一實現(xiàn)關(guān)閉中斷。

在這里可能有一個地方挺難理解,那就是這個關(guān)中斷的代碼是可嵌套的。那出現(xiàn)了兩個問題:

關(guān)中斷可嵌套有什么意義那?怎么實現(xiàn)的中斷可嵌套那?

先說一下中斷嵌套的意義,

其實大家分析一下,所謂保護臨界段代碼就是關(guān)中斷,在操作系統(tǒng)內(nèi)部本身就是關(guān)中斷、然后開中斷。這是沒有問題的,但大家想一下,操作系統(tǒng)給用戶提供了一些編程接口,當(dāng)用戶希望用同樣關(guān)中斷的方法來保護共享數(shù)據(jù)時。比如用戶寫的代碼如下:

關(guān)中斷

調(diào)用系統(tǒng)服務(wù)

用戶其他操作

開中斷

大家想一下,本來用戶想保存從用戶關(guān)中斷和開中斷這些代碼的,不過你調(diào)用了操作系統(tǒng)的服務(wù),如果你是用的方法是不可嵌套的,那在退出操作系統(tǒng)代碼時 已經(jīng)開中斷了。違背了用戶的愿望。所以我們操作系統(tǒng)提供可嵌套的關(guān) 開中斷的方法。這種嵌套是針對高層應(yīng)用程序的。在操作系統(tǒng)內(nèi)部這個嵌套是沒有任何意義的。在操作系統(tǒng)內(nèi)部一般在使用臨界資源之前關(guān)中斷,在使用完畢后開中 斷,OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()都是成對使用的。

下面分析中斷嵌套是怎么實現(xiàn)的:

先看兩個宏的定義:

#define  OS_ENTER_CRITICAL()  { cpu_sr = INTS_OFF(); }

#define  OS_EXIT_CRITICAL()   { if(cpu_sr == 0) INTS_ON(); }

再看INTS_OFF()這個函數(shù)的內(nèi)容:

INTS_OFF

   mrs   r0, cpsr          ; current CSR//讀取當(dāng)前CPSR的值

   mov   r1, r0           ; make a copy for masking//把CPSR的值復(fù)制給r1

   orr   r1, r1, #0xC0     ; mask off int bits  //屏蔽中斷

   msr   CPSR_cxsf, r1   ; disable ints (IRQ and FIQ)//把屏蔽中斷后的內(nèi)容重新寫會CPSR

   and   r0, r0, #0x80     ; return IRQ bit from original CSR//返回CPSR中原來的CPSR中I位的狀態(tài),由前面的ATPCS知識我們知道,函數(shù)的返回值在R0中

   mov   pc,lr             ; return

這個關(guān)中斷的函數(shù)執(zhí)行完畢后有兩個作用,一:實現(xiàn)了關(guān)閉中斷。二:記憶CPSR中原來的I位的狀態(tài),并將其保存在cpu_sr變量中。

開中斷的代碼中INTS_ON()這個函數(shù)的內(nèi)容就不多說了,很簡單,就是讀取CPSR的值改寫后重新寫回CPSR中。開中斷的宏定義的代碼中 if(cpu_sr == 0) INTS_ON();,首先判斷cpu_sr的值,若其值為0,開中斷,否則并不開中斷。其值為0說明你調(diào)用OS_ENTER_CRITICAL()這個 關(guān)閉中斷之前CPSR是開中斷的,即你只關(guān)閉了一次中斷。若你嵌套關(guān)閉中斷,在里層的開中斷并不執(zhí)行。例如:

你在你的應(yīng)用程序里定義了一個變量cpu_sr1用來存放你關(guān)中斷時返回的值。

Ucos內(nèi)核定義的變量是cpu_sr,用來存放你關(guān)中斷的返回值。

其代碼如下:

cpu_sr1 = INTS_OFF();//你的應(yīng)用程序要關(guān)閉中斷,如果這是第一次關(guān)閉中斷,那cpu_sr1的值是0

OS_ENTER_CRITICAL();//ucos內(nèi)核又關(guān)閉了一次中斷,這時cpu_sr的值肯定是1

OS_EXIT_CRITICAL();// 由于cpu_sr的值肯定是1,開中斷不執(zhí)行

其他用戶代碼

if(cpu_sr1 == 0) INTS_ON();//開中斷

 

 

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多