日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

【连载】从单片机到操作系统⑥——FreeRTOS任务切换机制详解

發布時間:2023/12/8 windows 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【连载】从单片机到操作系统⑥——FreeRTOS任务切换机制详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

大家晚上好,我是杰杰,最近挺忙的,好久沒有更新了,今天周末就吐血更新一下吧!

前言

? ? ?FreeRTOS是一個是實時內核,任務是程序執行的最小單位,也是調度器處理的基本單位,移植了FreeRTOS,則避免不了對任務的管理,在多個任務運行的時候,任務切換顯得尤為重要。而任務切換的效率會決定了系統的穩定性與效率。

? ? FreeRTOS的任務切換是干嘛的呢,rtos的實際是永遠運行的是具有最高優先級的運行態任務,而那些之前在就緒態的任務怎么變成運行態使其得以運行呢,這就是我們FreeRTOS任務切換要做的事情,它要做的是找到最高優先級的就緒態任務,并且讓它獲得cpu的使用權,這樣,它就能從就緒態變成運行態,這樣子,整個系統的實時性就會很好,響應也會很好,而不會讓程序阻塞卡死。

? ??要知道怎么實現任務切換,那就要知道任務切換的機制,在不同的cpumcu)中,觸發的方式可能會不一樣,現在是以Cortex-M3為例來講講任務的切換。為了大家能看懂本文,我就拋轉引玉一下,引用《Cortex-M3權威指南-中文版》的部分語句(如涉及侵權,請聯系杰杰刪除)

SVC??PendSV

? ? SVC(系統服務調用,亦簡稱系統調用)和 PendSVPended System Call,可懸起系統調用),它們多用于在操作系統之上的軟件開發中。SVC 用于產生系統函數的調用請求。例如,操作系統不讓用戶程序直接訪問硬件,而是通過提供一些系統服務函數,用戶程序使用 SVC 發出對系統服務函數的呼叫請求,以這種方法調用它們來間接訪問硬件。因此,當用戶程序想要控制特定的硬件時,它就會產生一個 SVC 異常,然后操作系統提供的 SVC 異常服務例程得到執行,它再調用相關的操作系統函數,后者完成用戶程序請求的服務。

???另一個相關的異常是 PendSV(可懸起的系統調用),它和 SVC 協同使用。一方面,SVC異常是必須立即得到響應的(若因優先級不比當前正處理的高,或是其它原因使之無法立即

響應,將上訪成硬 fault——譯者注),應用程序執行 SVC 時都是希望所需的請求立即得到響應。另一方面,PendSV 則不同,它是可以像普通的中斷一樣被懸起的(不像 SVC 那樣會上訪)。OS 可以利用它“緩期執行”一個異常——直到其它重要的任務完成后才執行動作。懸起 PendSV?的方法是:手工往 NVIC PendSV 懸起寄存器中寫 1。懸起后,如果優先級不夠高,則將緩期等待執行。

????如果一個發生的異常不能被即刻響應,就稱它被“懸起”(pending)。不過,少數 fault異常是不允許被懸起的。一個異常被懸起的原因,可能是系統當前正在執行一個更高優先級異常的服務例程,或者因相關掩蔽位的設置導致該異常被除能。對于每個異常源,在被懸起的情況下,都會有一個對應的“懸起狀態寄存器”保存其異常請求,直到該異常能夠執行為止,這與傳統的 ARM 是完全不同的。在以前,是由產生中斷的設備保持住請求信號。現在NVIC 的懸起狀態寄存器的出現解決了這個問題,即使后來設備已經釋放了請求信號,曾經的中斷請求也不會錯失。

系統任務切換的工程分析

???在系統中正常執行的任務(假設沒有外部中斷IRQ),用Systick直接做上下文切換是完全沒有問題的,如圖:


?但是問題是幾乎很少嵌入式的設備會不用其豐富的中斷響應,所以,直接用systick做系統的上下文切換那是不實際的,這存在很大的風險,因為假設systick打斷了一個中斷(IRQ),立即做出上下文切換的話,則觸犯用法 fault 異常,除了重啟你沒有其他辦法了,這樣子做出來的產品就是垃圾!!用我老板的話說就是寫的什么狗屎!!!如圖所示:


????那這么說這樣不行那也不行,怎么辦啊?請看看前面接介紹的PendSV,是不是有點豁然開朗了?PendSV 來完美解決這個問題。PendSV 異常會自動延遲上下文切換的請求,直到其它的 ISR 都完成了處理后才放行。為實現這個機制,需要把 PendSV 編程為最低優先級的異常。如果 OS 檢測到某 IRQ 正在活動并且被 SysTick 搶占,它將懸起一個 PendSV 異常,以便緩期執行上下文切換。

? ?懂了嗎?就是說,只要將PendSV的優先級設為最低的,systick即使是打斷了IRQ,它也不會馬上進行上下文切換,而是等到IRQ執行完,PendSV 服務例程才開始執行,并且在里面執行上下文切換。過程如圖所示:


任務切換的源碼實現

過程差不多了解了,那看看FreeRTOS中怎么實現吧!!

FreeRTOS有兩種方法觸發任務切換:

  • ??一種就是systick觸發PendSV異常,這是最經常使用的。

  • ?另一種是主動進行切換任務,執行系統調用,比如普通任務可以使用taskYIELD()強制任務切換,中斷服務程序中使用portYIELD_FROM_ISR()強制任務切換。

  • 1

    先說說第一種吧,就在systick中斷中調用xPortSysTickHandler();

    下面是源碼: void?xPortSysTickHandler(?void?) {vPortRaiseBASEPRI();{/*?Increment?the?RTOS?tick.?*/if(?xTaskIncrementTick()?!=?pdFALSE?){/*?A?context?switch?is?required.??Context?switching?is?performed?inthe?PendSV?interrupt.??Pend?the?PendSV?interrupt.?*/portNVIC_INT_CTRL_REG?=?portNVIC_PENDSVSET_BIT;}}vPortClearBASEPRIFromISR(); }

    ? ? ?它的執行過程是這樣子的,屏蔽所有中斷,因為SysTick以最低的中斷優先級運行,所以當這個中斷執行時所有中斷必須被屏蔽。vPortRaiseBASEPRI();就是屏蔽所有中斷的。而且并不需要保存本次中斷的值,因為systick的中斷優先級是已知的,執行完直接恢復所有中斷即可。

    ????xTaskIncrementTick()中會對tick的計數值進行自加,然后檢查有沒有處于就緒態的最優先級任務,如果有,則返回非零值,然后表示需要進行任務切換,而并非馬上進行任務切換,此處要注意,它只是向中斷狀態寄存器bit28位寫入1,只是將PendSV掛起,假如沒有比PendSV更高優先級的中斷,它才會進入PendSV中斷服務函數進行任務切換。

    ?#define?portNVIC_PENDSVSET_BIT????????(?1UL?<<?28UL?)

    然后解除屏蔽所有中斷。

    vPortClearBASEPRIFromISR();

    2

    ?另一種方法是主動進行任務切換,不管是使用taskYIELD()還是portYIELD_FROM_ISR(),最終都會執行下面的代碼:

    #define?portYIELD()????????????????????????????????????????????????????????????????\ {????????????????????????????????????????????????????????????????????????????????\/*?Set?a?PendSV?to?request?a?context?switch.?*/?????????????????????????????\portNVIC_INT_CTRL_REG?=?portNVIC_PENDSVSET_BIT;?????????????????????????????\???????????????????????????????????????????????????????????????????????__dsb(?portSY_FULL_READ_WRITE?);????????????????????????????????????????????\__isb(?portSY_FULL_READ_WRITE?);????????????????????????????????????????????\ }

    ? 這portYIELD()其實是一個宏定義來的。同樣是向中斷狀態寄存器bit28位寫入1,將PendSV掛起,然后等待任務的切換。

    具體的任務切換源碼

    ?????一直在說怎么進行任務切換的,好像還沒看到任務切換的源碼啊,哎,下面來看看任務切換的真面目!!

    __asm?void?xPortPendSVHandler(void) {extern?uxCriticalNesting;extern?pxCurrentTCB;extern?vTaskSwitchContext;PRESERVE8mrs?r0,?pspisbldr?r3,?=pxCurrentTCB???????/*?Get?the?location?of?the?current?TCB.?*/ldr?r2,?[r3]stmdb?r0!,?{r4-r11}?????????/*?Save?the?remaining?registers.?*/str?r0,?[r2]????????????????/*?Save?the?new?top?of?stack?into?the?first?member?of?the?TCB.?*/stmdb?sp!,?{r3,?r14}mov?r0,?#configMAX_SYSCALL_INTERRUPT_PRIORITYmsr?basepri,?r0dsbisbbl?vTaskSwitchContextmov?r0,?#0msr?basepri,?r0ldmia?sp!,?{r3,?r14}ldr?r1,?[r3]ldr?r0,?[r1]????????????????/*?The?first?item?in?pxCurrentTCB?is?the?task?top?of?stack.?*/ldmia?r0!,?{r4-r11}?????????/*?Pop?the?registers?and?the?critical?nesting?count.?*/msr?psp,?r0isbbx?r14nop }

    ? 不是我不想看,是我看到匯編就頭大啊,這幾天我也在看源碼,實在是頭大。

    ? 找到核心的函數看看就好啦,不管那么多,有興趣的可以研究一下中斷代碼,有不懂的也很歡迎你們來問我,一起研究研究,也是不錯的選擇。

    下面是看重點的地方了:

    mov?r0,?????????????#configMAX_SYSCALL_INTERRUPT_PRIORITY msr?basepri,?r0

    這兩句代碼是關閉中斷的。關中斷就得干活了,嘿嘿嘿~

    bl?vTaskSwitchContext

    BL是跳轉指令嘛,這個我還是有點懂的。

    ? 調用函數vTaskSwitchContext(),尋找新的任務運行,通過使變量pxCurrentTCB指向新的任務來實現任務切換,然后就是打開中斷,退出去了。

    尋找下一個要運行任務

    ? 是不是感覺沒什么大不了的樣子,如果你是這樣子覺得的,可能還沒學到家,趕緊去看看FreeRTOS的源碼,在config.h配置文件中是不是有一個叫做硬件查找下一個運行的任務呢?configUSE_PORT_OPTIMISED_TASK_SELECTION,這個在FreeRTOS中叫做特殊方法,其實也是硬件查找啦,但是并不是每種單片機都支持的,如果是不支持的話,只能選擇軟件查找的方法了,就是所謂的通用方法。通用方法我就不多說了,因為我用的是STM32,他是支持硬件方法的,這樣子效率更高,所以我也沒必要去研究他的軟件方法,假如有興趣的小伙伴可以研讀一下源碼,有不懂的可以向我提問,源碼如下:

    #define?taskSELECT_HIGHEST_PRIORITY_TASK()????????????????????????????????????????????????????????????\{???????????????????????????????????????????????????????????????????????????????????????????????????\UBaseType_t?uxTopPriority?=?uxTopReadyPriority;?????????????????????????????????????????????????????\\/*?Find?the?highest?priority?queue?that?contains?ready?tasks.?*/????????????????????????????????\while(?listLIST_IS_EMPTY(?&(?pxReadyTasksLists[?uxTopPriority?]?)?)?)???????????????????????????\{???????????????????????????????????????????????????????????????????????????????????????????????\configASSERT(?uxTopPriority?);??????????????????????????????????????????????????????????????\--uxTopPriority;????????????????????????????????????????????????????????????????????????????\}???????????????????????????????????????????????????????????????????????????????????????????????\\/*?listGET_OWNER_OF_NEXT_ENTRY?indexes?through?the?list,?so?the?tasks?of????????????????????????\the?same?priority?get?an?equal?share?of?the?processor?time.?*/??????????????????????????????????\listGET_OWNER_OF_NEXT_ENTRY(?pxCurrentTCB,?&(?pxReadyTasksLists[?uxTopPriority?]?)?);???????????\uxTopReadyPriority?=?uxTopPriority;?????????????????????????????????????????????????????????????\}?/*?taskSELECT_HIGHEST_PRIORITY_TASK?*/

    而硬件的方法源碼則在下面:

    ????#define?taskSELECT_HIGHEST_PRIORITY_TASK()??????????????????????????????????????????????????????\{???????????????????????????????????????????????????????????????????????????????????????????????\UBaseType_t?uxTopPriority;??????????????????????????????????????????????????????????????????????\\/*?Find?the?highest?priority?list?that?contains?ready?tasks.?*/?????????????????????????????\portGET_HIGHEST_PRIORITY(?uxTopPriority,?uxTopReadyPriority?);??????????????????????????????\configASSERT(?listCURRENT_LIST_LENGTH(?&(?pxReadyTasksLists[?uxTopPriority?]?)?)?>?0?);?????\listGET_OWNER_OF_NEXT_ENTRY(?pxCurrentTCB,?&(?pxReadyTasksLists[?uxTopPriority?]?)?);???????\}?/*?taskSELECT_HIGHEST_PRIORITY_TASK()?*/

    其方法是利用硬件提供的計算前導零指令CLZ,具體宏定義為:

    #define?portGET_HIGHEST_PRIORITY(?uxTopPriority,?uxReadyPriorities?)?uxTopPriority?=?(?31UL?-?(?uint32_t?)?__clz(?(?uxReadyPriorities?)?)?)

    ? 靜態變量uxTopReadyPriority包含了處于就緒態任務的最高優先級的信息,因為FreeRTOS運行的永遠是處于最高優先級的運行態,而下個處于最高優先級的就緒態則必定會在下次任務切換的時候運行,uxTopReadyPriority使用每一位來表示任務是否處于就緒態,比如變量uxTopReadyPrioritybit01,則表示存在優先級為0的任務處于就緒態,bit61則表示存在優先級為6的任務處于就緒態。并且,由于bit0的優先級高于bit6,那么下個任務就是bit0的任務運行了(數組越低優先級越高)。由于32位整形數最多只有32位,因此使用這種特殊方法限定最大可用優先級數目為32,即優先級0~31。得到了下個處于最高優先級就緒態任務了,就調用listGET_OWNER_OF_NEXT_ENTRY來獲取下一個任務的列表項,然后將該列表項的任務控制塊TCB賦值給pxCurrentTCB,那么我們就得到下一個要運行的任務了。

    至此,任務切換已經完成。

    END


    更多好文章請關注杰杰。

    【連載】從單片機到操作系統⑤——FreeRTOS列表&列表項的源碼解讀

    【連載】從單片機到操作系統④——FreeRTOS創建任務&開啟調度詳解

    ?【連載】從單片機到操作系統③——走進FreeRTOS

    ?STM32進階之串口環形緩沖區實現

    本文是杰杰原創,轉載請說明出處。本文直接引用了《Cortex-M3權威指南》的部分語句(如涉及侵權,請聯系杰杰刪除)。






    總結

    以上是生活随笔為你收集整理的【连载】从单片机到操作系统⑥——FreeRTOS任务切换机制详解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。