在线看毛片网站电影-亚洲国产欧美日韩精品一区二区三区,国产欧美乱夫不卡无乱码,国产精品欧美久久久天天影视,精品一区二区三区视频在线观看,亚洲国产精品人成乱码天天看,日韩久久久一区,91精品国产91免费

<abbr id="27omo"></abbr>

<menu id="27omo"><dl id="27omo"></dl></menu>
    • <label id="27omo"><tt id="27omo"></tt></label>

      新聞中心

      EEPW首頁 > 嵌入式系統(tǒng) > 牛人業(yè)話 > Qsys與uC/OS-II學(xué)習(xí)筆記5:任務(wù)切換

      Qsys與uC/OS-II學(xué)習(xí)筆記5:任務(wù)切換

      作者: 時間:2015-11-26 來源:網(wǎng)絡(luò) 收藏

        上個筆記提到調(diào)用任務(wù)延時函數(shù)后,系統(tǒng)將會進行任務(wù)切換,否則當(dāng)前運行任務(wù)就會一直霸占著CPU的使用權(quán)。那么這個任務(wù)延時函數(shù)中到底有什么奧秘?調(diào)用它為什么能夠讓任務(wù)切換自如?這個筆記咱就要揭開的一大設(shè)計精髓——任務(wù)切換。

      本文引用地址:http://www.biyoush.com/article/283455.htm

        特權(quán)同學(xué)并非軟件工程或是計算機科班出身,還真沒學(xué)過什么操作系統(tǒng),對于CPU內(nèi)部架構(gòu)和工作機制的理解和認(rèn)識完全靠自身的實踐、摸索加一些教科書的研讀。對于一些概念的闡述或許不夠?qū)I(yè),如果有些偏差也非常歡迎大家提出來加以糾正,但是我想這些“草根”式的圖文或許多少能夠幫助大家快速的理解和認(rèn)識一些工作機理,但愿“八九不離十”應(yīng)該是形容這種狀態(tài)比較合適的詞匯吧。其實如果能起到這樣的效果,那么對這些文章而言也就足夠。畢竟一板一眼、中規(guī)中矩的教科書我們看得太多了,真的是有些審美疲勞了。

        因為要說任務(wù)調(diào)度的機理,那么我們不得不先把幾乎所有嵌入式處理器相關(guān)的書籍中都會提及的中斷概念再提一下。雖然講中斷的書滿大街都是,但是我想像圖1這樣一個簡單示意圖就能夠把中斷說清楚的還真不多(怎么有點“王婆賣瓜自賣自夸”的嫌疑,臉紅中~~)。一個“裸奔”的CPU軟件,無非就是一個main函數(shù)里面while(1)中包辦所有功能,偶爾來個中斷響應(yīng)一些實時性要求較高的處理,僅此而已。那么,很顯然,中斷響應(yīng)時有一個脫離當(dāng)前main函數(shù)的舉動發(fā)生,想要讓中斷響應(yīng)前后CPU回到原有的main函數(shù)執(zhí)行狀態(tài),則必須有一些額外的工作要干,第3和6步的出棧、入棧便是。這個示意圖中,大家需要明白,當(dāng)某個函數(shù)或某些指令占用CPU時,意味著CPU中的寄存器存儲著和當(dāng)前處理狀態(tài)相關(guān)的各種中間數(shù)據(jù)信息;同樣的道理,當(dāng)中斷函數(shù)占有CPU數(shù)據(jù)時,CPU中的寄存器存儲著和中斷函數(shù)相關(guān)的各種中間數(shù)據(jù)。堆棧是專門開辟的一片存儲空間,用于和CPU寄存器相映射。一個在main函數(shù)中運行著的程序(包括在它的子函數(shù)中運行),如果被一個中斷信號打斷后去執(zhí)行中斷處理,最后返回,這樣一個過程,發(fā)生了以下一些事情:

        1. 應(yīng)用程序(即Main函數(shù))中執(zhí)行某條指令,此時應(yīng)用程序控制CPU的使用權(quán),表現(xiàn)為占有CPU寄存器。

        2. 一個中斷源產(chǎn)生,應(yīng)用程序停下了CPU的使用。

        3. 應(yīng)用程序停下來那一刻的CPU寄存器內(nèi)容被copy到堆棧中,即我們稱之為入棧操作。

        4. 程序轉(zhuǎn)到中斷處理函數(shù)執(zhí)行,表現(xiàn)為即將到來的下一時刻中斷處理函數(shù)占有CPU使用權(quán)。

        5. 中斷處理函數(shù)擁有CPU使用權(quán),表現(xiàn)為占有CPU寄存器,直到中斷處理函數(shù)執(zhí)行完成。

        6. CPU寄存器恢復(fù)執(zhí)行入棧操作前的狀態(tài),即從堆棧中copy之前入棧的信息,我們稱之為出棧。

        7. 應(yīng)用程序回到中斷前的下一條指令開始執(zhí)行,雖然經(jīng)過2-6步的“意外事件”,但是除了應(yīng)用程序比預(yù)期延時了一小段時間執(zhí)行外,好像這個“意外事件”沒有發(fā)生過一樣。



        圖1

        再來看中的任務(wù)切換是如何實現(xiàn)的,應(yīng)該說,和傳統(tǒng)CPU的中斷機制有著異曲同工之妙。說白了,其實也是假借中斷之名偷梁換柱般完成了任務(wù)的切換。因為uC/OS-II中的每個task都好比“裸奔”著的軟件程序中的main函數(shù),他們都有機會獨立的占用CPU的使用權(quán)。Task的寫法通常有兩種:

        void user_task(void* pdata)

        {

        while (1)

        {

        //用戶代碼

        }

        }

        或者

        void user_task(void* pdata)

        {

        //用戶代碼

        OSTaskDel(OS_PRIO_SELF); //刪除當(dāng)前任務(wù)

        }

        前者我們已經(jīng)接觸過,在用戶代碼的最后我們通常也會加上任務(wù)延時函數(shù),讓出CPU的控制權(quán)。而后者相當(dāng)于一次性完成的任務(wù),執(zhí)行過一次該任務(wù)后,自我刪除,從此銷聲匿跡,除非該任務(wù)在其他任務(wù)函數(shù)中被重新建立恢復(fù)。

        OSTaskDel();函數(shù)

        INT8U OSTaskDel (INT8U prio);

        當(dāng)任務(wù)創(chuàng)建并由OSStart()函數(shù)啟動后,要么處于運行態(tài)(同一時刻有且只有一個運行態(tài)的任務(wù)),要么處于就緒態(tài),如果處于某個正在運行的任務(wù)使用OSTaskDel()函數(shù)刪除了該任務(wù)本身或者其他任務(wù)(空閑任務(wù)OSTaskIdle()是唯一不能被刪除的任務(wù)),那么被刪除的任務(wù)并不是從存儲代碼的程序中物理消失了,這段代碼還在,只不過它已經(jīng)不在任務(wù)切換優(yōu)先級列表中了,以后的任務(wù)切換中不會考慮運行該任務(wù),我們說這種狀態(tài)叫做休眠態(tài),如果要從休眠態(tài)喚醒到就緒態(tài),則需要重新創(chuàng)建該函數(shù)。

        OSTaskIdle()函數(shù)

        空閑任務(wù)是在OSInit();函數(shù)中被建立的,看這個函數(shù)的具體內(nèi)容,發(fā)現(xiàn)它其實并沒有干什么大事,無非是在那里“消磨時間”,也的確是這樣。但uC/OS-II中必須建立空閑函數(shù),而且它的優(yōu)先級一定是最低的,至于為什么,我們接下來先講講任務(wù)切換的機理,然后大家很容易就能明白的。

        void OS_TaskIdle (void *p_arg)

        {

        #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */

        OS_CPU_SR cpu_sr = 0;

        #endif

        (void)p_arg; /* Prevent compiler warning for not using 'p_arg' */

        for (;;) {

        OS_ENTER_CRITICAL();

        OSIdleCtr++;

        OS_EXIT_CRITICAL();

        OSTaskIdleHook(); /* Call user definable HOOK */

        }

        }

        如圖2所示,這便是任務(wù)切換的大體流程。和中斷很相似,這里假設(shè)task1要切換到task2,task1中一定會調(diào)用任務(wù)延時函數(shù)(上一個筆記已經(jīng)提到,這是任務(wù)切換的必要條件),咱還是簡單的12345把它說明白:

        1. Task1擁有CPU的控制權(quán),當(dāng)前CPU寄存器存儲著和task1當(dāng)前執(zhí)行指令相關(guān)的信息。

        2. Task1調(diào)用了任務(wù)延時函數(shù)。

        3. CPU寄存器的數(shù)據(jù)信息被copy到了堆棧中,即入棧,這個堆棧是task1獨有的,圣神不可侵犯。

        4. 把Task2獨有的堆棧數(shù)據(jù)信息paste到CPU寄存器中,即出棧,這是要恢復(fù)task2在上一次擁有CPU控制權(quán)的最后一條執(zhí)行指令留下的現(xiàn)場。當(dāng)然也可能task2之前未曾擁有過CPU控制權(quán),沒關(guān)系,那么默認(rèn)這個堆棧是應(yīng)該是個空的。

        5. 模擬產(chǎn)生了一個軟中斷源產(chǎn)生,任務(wù)延時函數(shù)被中斷,停止繼續(xù)執(zhí)行。

        6. 模擬中斷處理函數(shù),大概這個函數(shù)中什么都不做,為的是有一個中斷返回的動作(還真有點買櫝還珠的味道)。

        7. 中斷返回時,當(dāng)前的CPU寄存器是task2的工作現(xiàn)場,那么這意味著task2已經(jīng)擁有了CPU的控制權(quán)。怎么樣?真得是被“偷梁換柱”了,CPU已經(jīng)從task1的while(1)里面被“俘虜”到了task2中。至此,一次任務(wù)切換完成。



        圖2

        大家可以好好消化一下,其實任務(wù)的切換還真的不是傳說中那么神奇。到這里,還是沒有把任務(wù)延時函數(shù)的神秘面紗揭開,沒關(guān)系,一個一個來,咱要各個擊破,每個知識點都吃透了才行。

        我們先給出任務(wù)延時的一個最基本函數(shù)OSTimeDly()的程序,注意看該函數(shù)的最后調(diào)用了OS_Sched()函數(shù),該函數(shù)便是CPU任務(wù)切換的“罪魁禍?zhǔn)住?,好奇心強的朋友可不能放過它。

        void OSTimeDly (INT16U ticks)

        {

        INT8U y;

        #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */

        OS_CPU_SR cpu_sr = 0;

        #endif

        if (OSIntNesting > 0) { /* See if trying to call from an ISR */

        return;

        }

        if (ticks > 0) { /* 0 means no delay! */

        OS_ENTER_CRITICAL();

        y = OSTCBCur->OSTCBY; /* Delay current task */

        OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;

        if (OSRdyTbl[y] == 0) {

        OSRdyGrp &= ~OSTCBCur->OSTCBBitY;

        }

        OSTCBCur->OSTCBDly = ticks; /* Load ticks in TCB */

        OS_EXIT_CRITICAL();

        OS_Sched(); /* Find next task to run! */

        }

        }

        OS_Sched()函數(shù)完成任務(wù)級的調(diào)度,該函數(shù)完成了前一個任務(wù)CPU寄存器的入棧和后一個任務(wù)CPU寄存器的出棧,并且在最后做了一次“模擬”中斷返回的操作,這個操作是由OS_TASK_SW()函數(shù)里完成的。

        void OS_Sched (void)

        {

        #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */

        OS_CPU_SR cpu_sr = 0;

        #endif

        OS_ENTER_CRITICAL();

        if (OSIntNesting == 0) { /* Schedule only if all ISRs done and ... */

        if (OSLockNesting == 0) { /* ... scheduler is not locked */

        OS_SchedNew();

        if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */

        OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];

        #if OS_TASK_PROFILE_EN > 0

        OSTCBHighRdy->OSTCBCtxSwCtr++; /* Inc. # of context switches to this task */

        #endif

        OSCtxSwCtr++; /* Increment context switch counter */

        OS_TASK_SW(); /* Perform a context switch */

        }

        }

        }

        OS_EXIT_CRITICAL();

        }

        函數(shù)void OSCtxSw(void);咱還真無法右鍵open打開,通常不是用C語言寫的,而是用匯編來完成這個操作。

        再回到 OSTaskIdle()函數(shù),試想想如果系統(tǒng)中只有兩個用戶任務(wù)task1和task2,如果他們都調(diào)用任務(wù)延時函數(shù),那么模擬中斷返回后系統(tǒng)應(yīng)該到哪里繼續(xù)執(zhí)行程序呢?不得而知,或許程序就要跑飛了,基于此,OSTaskIdle()函數(shù)就有存在的必要了,雖然它好像不干什么事,但至少它能保證系統(tǒng)在沒有task可執(zhí)行的時候處于一個可控的狀態(tài)中。除此以外,OSTaskIdle()函數(shù)中還做了一件或許大家多少還是有些在意的CPU使用率的計算,它是通過計算空閑時間來推斷每秒鐘CPU的使用率的。



      關(guān)鍵詞: Qsys uC/OS-II

      評論


      相關(guān)推薦

      技術(shù)專區(qū)

      關(guān)閉