# RTOS 學習筆記 - 1 > 參考資源:[科技下午茶 - RTOS-01](https://www.youtube.com/watch?v=a0jo2wJXhgU&list=PLJvU73y7BpTsNAHgz0KoRtcyq16xflK9K)。 ## OS v.s. Micro Kernel ### Linux 架構及名詞解釋 ![圖片](https://hackmd.io/_uploads/rkezF6I0A.png =70%x) - Open System:隨時能下載新的應用程式(如 Linux、Windows、Android)。 - Closed System:產品出貨之後,Application 就定了,不能再下載新的應用程式(如大部分的嵌入式系統)。 - Dynamic Linking:OS 本身所提供的 API 或服務可以在出貨之後「再更新」(如 Windows Update)。於 Windows 可用 DLL 達成;在 Linux 則有 Shell library 的概念。 - Static Linking:大部分簡單的 OS。API 無法再增加。 - Monolithic Kernel:如 Linux(百萬行程式碼),是單核心的龐大設計,效率較佳;但維護起來較麻煩。 - Micro Kernel:約幾千行,此設計下的 Kernel 只提供基本服務(Task、IPC、Memory),調整上較快速且穩定、容易維護。大部分 MCU 市場皆屬於此設計。 ### RTOS for MCU - MCU 的常見設計為「單核」且「無 MMU(Memory Management Unit),意味著無 Virtual Address」。 - 多數 RTOS 僅提供 - Task Management(Create/Delete, Scheduler, Priority, etc.) - IPC(Queue, Event, Semaphore, etc.) - Memory services > 故本質上應歸類於 Micro Kernel。 - 以 Micro Kernel 搭配獨立的 Services,理論上可架構出完整的 OS,但在 MCU 的應用上幾乎未見。 - MCU 的軟體架構(如 CMSIS、NXP-SDK)中,RTOS 多以「模組」的方式存在,其定位如同 Vendor APIs,處於平行的層級。 - 對於複雜的需求,透過 RTOS 的多緒能力,可明顯降低設計上的負擔。如啟用常駐服務、背景監聽等。 ## C v.s. ARM ![圖片](https://hackmd.io/_uploads/BJu7lCIRC.png =80%x) > C 語言的成功,讓 ARM 在設計其 Instructions 時,針對 C 的特性去作最佳化。 高階語言設計與 CPU 架構發展時,會有「互相參考」的情形。 ## Context Switch Context Switch:Task 之間的交換執行。 ![圖片](https://hackmd.io/_uploads/HkmwWCLRA.png =80%x) > FreeRTOS:在 Task 的環境保存方面,只用到 2 Blocks,即 TCB(Task Control Block)-放 Block 的基本資訊(如 Task 名稱、堆疊大小、優先級等);Stack-放暫存器內容。 ### 程式解析(以 Cortex-M 為例) 參照 [FreeRTOSConfig.h](https://github.com/Infineon/freertos/blob/master/Source/portable/COMPONENT_CM33/FreeRTOSConfig.h) 之三種 ISR 如下,以便說明 Context Switch 運作原理: ```c /* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS standard names - or at least those used in the unmodified vector table. */ #define vPortSVCHandler SVC_Handler #define xPortPendSVHandler PendSV_Handler #define xPortSysTickHandler SysTick_Handler ``` - `SVC_Handler`:用於啟動第一個 Task,同時設定 PSP。 - `SysTick_Handler`:當 TIME_SLICING 啟動後,可用於強制切換 Task(取決於優先級)。 - `PendSV_Handler`:Context Switch 執行之處。 > 此三種 ISR 皆為 Cortex-M 內建。 ### `SysTick_Handler` 觀察 [port.c](https://github.com/Infineon/freertos/blob/master/Source/portable/COMPONENT_CM4/TOOLCHAIN_ARM/port.c#L508) 中,`xPortSysTickHandler` 的函式內容: ```c void xPortSysTickHandler( void ) { /* The SysTick runs at the lowest interrupt priority, so when this interrupt * executes all interrupts must be unmasked. There is therefore no need to * save and then restore the interrupt mask value as its value is already * known. */ portDISABLE_INTERRUPTS(); { /* Increment the RTOS tick. */ if( xTaskIncrementTick() != pdFALSE ) { /* A context switch is required. Context switching is performed in * the PendSV interrupt. Pend the PendSV interrupt. */ portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; } } portENABLE_INTERRUPTS(); } ``` 進出函式時,會分別「將中斷關閉 / 開啟」,因函式中段是一 Critical section,可能執行 Task 切換動作。 `xTaskIncrementTick` 函式透過 tick+1 來實現 Task Switching,若發現有 Task 需要交換,則回傳 `true` 並紀錄一些狀態。接著透過設定 `portNVIC_PENDSVSET_BIT`,由 PendSV 接手執行。 ### `PendSV_Handler` 同樣在 port.c 中,存在 `xPortPendSVHandler` 函式如下: ```c void xPortPendSVHandler( void ) { __asm volatile ( " mrs r0, psp \n" " isb \n" " \n" " ldr r3, pxCurrentTCBConst \n"/* Get the location of the current TCB. */ " ldr r2, [r3] \n" " \n" " tst r14, #0x10 \n"/* Is the task using the FPU context? If so, push high vfp registers. */ " it eq \n" " vstmdbeq r0!, {s16-s31} \n" " \n" " stmdb r0!, {r4-r11, r14} \n"/* Save the core registers. */ " str r0, [r2] \n"/* Save the new top of stack into the first member of the TCB. */ " \n" " stmdb sp!, {r0, r3} \n" " mov r0, %0 \n" " msr basepri, r0 \n" " dsb \n" " isb \n" " bl vTaskSwitchContext \n" " mov r0, #0 \n" " msr basepri, r0 \n" " ldmia sp!, {r0, r3} \n" " \n" " ldr r1, [r3] \n"/* The first item in pxCurrentTCB is the task top of stack. */ " ldr r0, [r1] \n" " \n" " ldmia r0!, {r4-r11, r14} \n"/* Pop the core registers. */ " \n" " tst r14, #0x10 \n"/* Is the task using the FPU context? If so, pop the high vfp registers too. */ " it eq \n" " vldmiaeq r0!, {s16-s31} \n" " \n" " msr psp, r0 \n" " isb \n" " \n" " bx r14 \n" " \n" "pxCurrentTCBConst: .word pxCurrentTCB \n" ::"i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) ); } ``` > 注意,若換成 RISC-V 等,則實作上會截然不同。 Context Switch 流程可如下呈現: ![圖片](https://hackmd.io/_uploads/HycB0RLAC.png =70%x) ![圖片](https://hackmd.io/_uploads/Sk4u00LRR.png =60%x)