從零開始構(gòu)建實(shí)時(shí)操作系統(tǒng)—任務(wù)切換
1、前言
隨著計(jì)算機(jī)技術(shù)和微電子技術(shù)的迅速發(fā)展,嵌入式系統(tǒng)應(yīng)用領(lǐng)域越來越廣泛,尤其是其具備低功耗技術(shù)的特點(diǎn)得到人們的重視。隨著工信部提出NB-IoT基站建設(shè)具體目標(biāo)、三大運(yùn)營(yíng)商加速建設(shè),即將迎來萬物互聯(lián)的新時(shí)代,這是信息產(chǎn)業(yè)繼移動(dòng)互聯(lián)網(wǎng)之后的下一個(gè)萬億級(jí)市場(chǎng),這些為實(shí)時(shí)操作系統(tǒng)的應(yīng)用提供了廣闊的前景。
嵌入式實(shí)時(shí)操作系統(tǒng)將會(huì)部署到越來越多的設(shè)備中,這就要求工程師深入地了解嵌入式實(shí)時(shí)操作系統(tǒng)。本系列文章將和大家一起從零開始構(gòu)建一個(gè)嵌入式實(shí)時(shí)操作系統(tǒng),我將用最簡(jiǎn)單直白的方式一步一步搭建,我將用一篇文章的方式來總結(jié)搭建中的每個(gè)節(jié)點(diǎn)階段,并開源軟件工程和源代碼。
2、嵌入式實(shí)時(shí)操作系統(tǒng)
嵌入式實(shí)時(shí)操作系統(tǒng)是一個(gè)特殊的程序,是一個(gè)支持多任務(wù)的運(yùn)行環(huán)境。嵌入式實(shí)時(shí)操作系統(tǒng)最大的特點(diǎn)就是“實(shí)時(shí)性”,如果有一個(gè)任務(wù)需要執(zhí)行,實(shí)時(shí)操作系統(tǒng)會(huì)立即執(zhí)行該任務(wù),不會(huì)有較長(zhǎng)的延時(shí)。典型的實(shí)時(shí)操作系統(tǒng)有uCOS ,RT-Thread,F(xiàn)reeRTOS ,VxWorks,WinCE等。
嵌入式實(shí)時(shí)操作系統(tǒng)是一個(gè)特殊的程序(通常稱為內(nèi)核),它可以創(chuàng)建和控制所有任務(wù)。嵌入式實(shí)時(shí)操作系統(tǒng)除了包含一個(gè)內(nèi)核以外,還提供其他服務(wù),如文件系統(tǒng),協(xié)議棧,圖形用戶界面等。本文的重點(diǎn)在于了解嵌入式實(shí)時(shí)操作系統(tǒng)內(nèi)核的工作原理和結(jié)構(gòu),因此文中提到的實(shí)時(shí)操作系統(tǒng)通常指的是操作系統(tǒng)內(nèi)核。實(shí)時(shí)操作系統(tǒng)內(nèi)核通常要占用5%左右的CPU運(yùn)行時(shí)間,另外內(nèi)核是一個(gè)軟件代碼,需要額外占用ROM空間和RAM空間。
嵌入式實(shí)時(shí)操作系主要由以下3個(gè)子系統(tǒng)組成:
- 任務(wù)調(diào)度子系統(tǒng)
- 任務(wù)通信子系統(tǒng)
- 內(nèi)存管理子系統(tǒng)
3、實(shí)現(xiàn)目標(biāo)
本文講解構(gòu)建嵌入式實(shí)時(shí)操作系統(tǒng)的第一個(gè)節(jié)點(diǎn)階段:實(shí)現(xiàn)簡(jiǎn)單的任務(wù)切換功能。
代碼區(qū)的數(shù)據(jù)是不變的,處理器寄存器的值和??臻g的值決定程序運(yùn)行狀態(tài)。讓每個(gè)任務(wù)“獨(dú)享”一個(gè)??臻g,當(dāng)我們將任務(wù)運(yùn)行時(shí)的處理器寄存器的值保存起來時(shí),這樣就實(shí)現(xiàn)保存任務(wù)的運(yùn)行狀態(tài)。同樣的當(dāng)我們把保存的任務(wù)運(yùn)行時(shí)的處理器寄存器的值裝載到處理的寄存器中時(shí),這樣就恢復(fù)了任務(wù)的運(yùn)行狀態(tài),任務(wù)繼續(xù)運(yùn)行起來。
切換任務(wù)的原理是:每個(gè)任務(wù)有一個(gè)“獨(dú)享”棧空間,通過保存和裝載任務(wù)運(yùn)行時(shí)的處理器寄存器的值,實(shí)現(xiàn)任務(wù)的暫停和恢復(fù)運(yùn)行。暫停一個(gè)任務(wù)后再恢復(fù)另外一個(gè)任務(wù)就完成了一次任務(wù)切換。
任務(wù)代碼,任務(wù)??臻g和處理器狀態(tài)如下圖:
4、實(shí)驗(yàn)環(huán)境
硬件是基于意法半導(dǎo)體的STM32F401(ARM公司的Cortex-M4內(nèi)核),軟件開發(fā)使用的是KEIL V5.2 開發(fā)工具。
軟件工程如下:
軟件工程中包含:main.c ,startup_stm32f401xc.s 和 readme三個(gè)文件。startup_stm32f401xc.s文件為STM32F401的啟動(dòng)文件,main.c文件實(shí)現(xiàn)任務(wù)切換功能,readme文件用于記錄版本修改日志。
5、代碼實(shí)現(xiàn)
切換任務(wù)的原理是讓每個(gè)任務(wù)都有一個(gè)“獨(dú)享”??臻g,通過保存和裝載任務(wù)運(yùn)行時(shí)的處理器寄存器的值,實(shí)現(xiàn)任務(wù)的暫停和恢復(fù)運(yùn)行。暫停一個(gè)任務(wù)后再恢復(fù)另外一個(gè)任務(wù)就完成了一次任務(wù)切換。
因此需要實(shí)現(xiàn):
- 每個(gè)任務(wù)的獨(dú)立??臻g。
- 實(shí)現(xiàn)任務(wù)的暫停和恢復(fù)。
- 實(shí)現(xiàn)任務(wù)的調(diào)度。
(1)實(shí)現(xiàn)獨(dú)立??臻g
棧空間代碼如下:
為每個(gè)任務(wù)定義一個(gè)靜態(tài)數(shù)組,當(dāng)任務(wù)運(yùn)行時(shí)將處理器的棧指針指向任務(wù)“自己的”靜態(tài)數(shù)組,從而實(shí)現(xiàn)獨(dú)立??臻g。棧空間用來存放局部變量,中斷調(diào)用和函數(shù)調(diào)用時(shí)的處理器寄存器的值。任務(wù)切換時(shí)需要將處理器寄存器的值保存到任務(wù)的獨(dú)立棧空間。
在保存任務(wù)運(yùn)行狀態(tài)時(shí)需要保存處理器寄存器值到棧空間,因此需要深入了解處理器寄存器的用途和出入棧順序,Cortex-M4內(nèi)核的寄存器和寄存器中斷自動(dòng)入棧的順序圖如下:
初始化??臻g的代碼如下:
??臻g初始化后的狀態(tài)如下:
棧是一中先入后出的數(shù)據(jù)結(jié)構(gòu),Cortex-M4內(nèi)核的棧操作方式倍設(shè)置成了向下生長(zhǎng)。psp_array用于保存任務(wù)棧指針,psp_array[0]任務(wù)0棧指針指向task0_stack[112],其中task0_stack[116]保存PC程序指針值,task0_stack[117]保存狀態(tài)寄存器(符合Cortex-M4內(nèi)核寄存器出棧順序:手動(dòng)出棧8個(gè)寄存器,硬件自動(dòng)出棧8個(gè)寄存器)。
(2)實(shí)現(xiàn)任務(wù)的暫停和恢復(fù)
代碼如下:
cortex-M4內(nèi)核有一個(gè)PendSV(可掛起的系統(tǒng)調(diào)用)異常,其異常編號(hào)為14并且具有可編程的優(yōu)先級(jí)。當(dāng)軟件將PendSV設(shè)置成掛起時(shí),程序?qū)⑦M(jìn)入PendSV異常(中斷)。
將PendSV異常優(yōu)先級(jí)設(shè)置為最低,其它中斷函數(shù)都可以得到正常響應(yīng),不會(huì)受到PendSV異常影響,在PendSV異常中執(zhí)行任務(wù)切換,時(shí)序框圖如下:
PendSV_Handler為Cortex-M4內(nèi)核中斷服務(wù)函數(shù),進(jìn)入中斷函數(shù)時(shí)處理器自動(dòng)保存了R0,R1,R2,R3, R12,LR,PC,XPSR,在PendSV_Handler中斷程序中完成R4~R11入棧保存工作,從而實(shí)現(xiàn)任務(wù)保存工作。
/* 讀取當(dāng)前進(jìn)程棧指針數(shù)值 */
MRS R0,PSP
/* 保存R4-R11八個(gè)寄存器的值到當(dāng)前任務(wù)棧中 同時(shí)將回寫的地址寫入R0 */
STMDB R0!,{R4-R11}
psp_array[0]為任務(wù)0的棧指針, psp_array[1]為任務(wù)1的棧指針。以下代碼實(shí)現(xiàn)任務(wù)棧指針切換。
/* 讀取psp_array 地址 */
LDR R3, =__cpp(&psp_array)
/* 將當(dāng)前進(jìn)程PSP指針值 寫入 相應(yīng)的 PSP_array 位置 */
STR R0,[R3,R2,LSL #2]
/* 獲取下個(gè)進(jìn)程序號(hào) */
LDR R4,=__cpp(&next_task)
LDR R4,[R4]
/* R1為&curr_task 將下個(gè)進(jìn)程序號(hào)寫入curr_task中 */
STR R4,[R1]
/* psp_array讀取更新后的curr_task的PSP指針數(shù)值 */
LDR R0,[R3,R4,LSL #2]
在PendSV_Handler中斷程序中完成R4~R11寄存器出棧,PendSV_Handler中斷程序返回時(shí)處理器自動(dòng)出棧R0,R1,R2,R3, R12,LR,PC,XPSR,從而實(shí)現(xiàn)任務(wù)恢復(fù)工作。
/* 出棧 R4-R11八個(gè)寄存器 */
LDMIA R0!,{R4-R11}
/* 設(shè)置PSP指針 */
MSR PSP,R0
/* 中斷返回 */
BX LR
(3)實(shí)現(xiàn)任務(wù)的調(diào)度
任務(wù)調(diào)度的代碼如下:
SysTick_Handler為定時(shí)器中斷程序,實(shí)現(xiàn)時(shí)間片輪流改變目標(biāo)任務(wù),并掛起PendSV_Handle中斷,退出SysTick_Handler中斷程序時(shí)進(jìn)入PendSV_Handle中斷程序。
6、運(yùn)行結(jié)果
代碼仿真運(yùn)行如下:
運(yùn)行代碼后task_num0和task_num1這兩個(gè)變量依次自加,代碼實(shí)現(xiàn)任務(wù)輪流切換功能。