1. LiteOS內核的任務管理
Huawei LiteOS 內核提供任務的創(chuàng)建、刪除、延遲、掛起、恢復等功能,以及鎖定和解鎖任務調度,支持任務按優(yōu)先級高低的搶占調度及同優(yōu)先級時間片輪轉調度。
1.1. 任務
在 LiteOS 中,一個任務就是一個線程,多個任務按照優(yōu)先級進行搶占式調度,達到多個任務“同時”運行的目的。
1.2. 任務的狀態(tài)
Huawei LiteOS 系統中的每個任務都有多種運行狀態(tài)。當系統初始化完成并啟動調度器后,系統中所有創(chuàng)建的任務就由內核進行調度,在不同運行狀態(tài)之間切換,同時在系統中競爭一定的資源。
任務的狀態(tài)有以下四種:
運行(Running):該任務正在執(zhí)行;
阻塞(Blocked):該任務不在就緒列表中。包含任務被掛起、任務被延時、任務正在等待信號量、讀寫隊列或者等待讀寫事件等;
退出態(tài)(Dead):該任務運行結束,等待系統回收資源。
1.3. 任務ID
任務 ID?在任務創(chuàng)建時通過參數返回給用戶,作為任務的一個非常重要的標識。
用戶可以通過任務ID對指定任務進行任務掛起、任務恢復、查詢任務名等操作。
1.4. 任務優(yōu)先級
優(yōu)先級表示任務執(zhí)行的優(yōu)先順序。任務的優(yōu)先級決定了在發(fā)生任務切換時即將要執(zhí)行的任務,在就緒列表中的最高優(yōu)先級的任務將得到執(zhí)行。
Huawei LiteOS 的任務一共有 32 個優(yōu)先級 (0-31),最高優(yōu)先級為 0,最低優(yōu)先級為 31。
因為是LiteOS的內核是搶占式調度內核,所以:
高優(yōu)先級的任務可打斷低優(yōu)先級任務,低優(yōu)先級任務必須在高優(yōu)先級任務阻塞或結束后才能得到調度。
1.5. 任務入口函數
任務入口函數是每個新任務得到調度后將執(zhí)行的函數,該函數由用戶實現,在任務創(chuàng)建時,通過任務創(chuàng)建結構體指定。
1.6. 多任務運作背后的機制
在多任務操作系統的內核中,為了方便對每個任務進行管理,每一個任務都有一個任務控制塊(TCB),其中包含了任務上下文棧指針(stack pointer)、任務狀態(tài)、任務優(yōu)先級、任務ID、任務名、任務棧大小等信息,TCB 相當于每個任務在內核中的身份證,可以反映出每個任務運行情況。
那么,操作系統中這么多的任務,它們依靠TCB被系統統一管理,那么又是如何被系統執(zhí)行的呢?
其實,每個任務相當于一個裸機程序,每個任務之間相互獨立,這個“獨立”指的是每個任務都有自己的運行環(huán)境 —— ??臻g,稱為任務棧,棧空間里保存的信息包含局部變量、寄存器、函數參數、函數返回地址等。
可是,系統中只有一個CPU,即使每個任務的任務棧是獨立的,可是多個任務都需要被同一個CPU所執(zhí)行,CPU的資源是共用的吧。
對的,CPU的資源是多個任務共用的,這些CPU的寄存器只有在任務執(zhí)行的時候被使用,稱為任務上下文,因此,內核在任務切換時會將切出任務的上下文信息保存在自身的任務??臻g里面,以便任務恢復時還原現場,從而在任務恢復后在切出點繼續(xù)開始執(zhí)行。
用戶創(chuàng)建任務時,系統會先申請任務控制塊需要的內存空間,申請成功后,系統會將任務棧進行初始化,預置上下文。此外,系統還會將“任務入口函數”地址放在相應位置。這樣在任務第一次啟動進入運行態(tài)時,將會執(zhí)行“任務入口函數”。
2. 任務管理API
Huawei LiteOS 任務管理模塊提供任務創(chuàng)建、任務刪除、任務延時、任務掛起和任務恢復、更改任務優(yōu)先級、鎖任務調度和解鎖任務調度、根據任務控制塊查詢任務 ID、根據 ID 查詢任務控制塊信息功能。
Huawei LiteOS 任務管理提供的 API 都是以?LOS?開頭,但是這些 API 使用起來比較復雜,所以本文中我們使用 Huawei IoT Link SDK 提供的統一API接口進行實驗,這些接口底層已經使用 LiteOS 提供的API實現,對用戶而言更為簡潔,API列表如下:
osal的api接口聲明在
任務相關的接口定義在osal.c中,基于LiteOS的接口實現在?liteos_imp.c文件中:
osal_task_create | 創(chuàng)建任務 |
osal_task_kill | 刪除任務(非自身) |
osal_task_exit | 任務退出 |
osal_task_sleep | 任務休眠 |
接口名 | 功能描述 |
---|
2.1. osal_task_create
osal_task_create的接口用于創(chuàng)建一個任務,其接口原型如下:
void*?osal_task_create(const?char?*name,int?(*task_entry)(void?*args),??????????????????????void?*args,int?stack_size,void?*stack,int?prior){????void?*ret?=?NULL;????if((NULL?!=?s_os_cb)?&&(NULL?!=?s_os_cb->ops)?&&(NULL?!=?s_os_cb->ops->task_create)) ????{ ????????ret?=?s_os_cb->ops->task_create(name,?task_entry,args,stack_size,stack,prior); ????}????return?ret; }
該接口的參數說明如下表:
name | 任務名稱 |
tsak_entry | 任務入口函數的函數指針 |
args | 任務入口函數的參數列表 |
stack_size | 任務棧大小 |
stack | 任務棧地址 |
prior | 任務優(yōu)先級 |
返回值 | 任務ID |
參數名稱 | 參數說明 |
---|
2.2. osal_task_kill
osal_task_kill用于刪除某個其他任務(非自身),其接口原型如下:
int?osal_task_kill(void?*task){????int?ret?=?-1;????if((NULL?!=?s_os_cb)?&&(NULL?!=?s_os_cb->ops)?&&(NULL?!=?s_os_cb->ops->task_kill)) ????{ ????????ret?=?s_os_cb->ops->task_kill(task); ????}????return?ret; }
該接口的參數說明如下表:
task | 任務ID |
返回值 | 0-刪除成功 |
返回值 | -1-刪除失敗 |
參數名稱 | 參數說明 |
---|
2.3. osal_task_exit
osal_task_exit接口用于任務退出(自身),目前暫未支持,可以直接return退出。
2.4. osal_task_sleep
osal_task_sleep接口用于任務主動休眠,單位是ms,其接口原型如下:
void?osal_task_sleep(int?ms){????if((NULL?!=?s_os_cb)?&&(NULL?!=?s_os_cb->ops)?&&(NULL?!=?s_os_cb->ops->task_sleep)) ????{ ????????s_os_cb->ops->task_sleep(ms); ????}????return?; }
3. 動手實驗 —— 體驗任務的創(chuàng)建與切換
實驗內容
本實驗中將創(chuàng)建兩個任務,一個低優(yōu)先級任務task1,一個高優(yōu)先級任務task2,兩個任務都會每隔2s在串口打印自己的任務id號,在串口終端中觀察兩個任務的運行情況。
實驗代碼
首先打開之前創(chuàng)建的 HelloWorld 工程,基于此工程進行實驗。
在Demo文件夾右擊,選擇新建文件夾:
新建osal_kernel_demo文件夾,用于存放內核的實驗文件:
接下來在此osal_kernel_demo文件夾中新建第一個實驗文件osal_task_demo.c文件,開始編寫代碼:
/*?使用osal接口需要包含該頭文件?*/#include?
編寫完成之后,要將我們編寫的osal_task_demo.c文件添加到makefile中,加入整個工程的編譯:
這里有個較為簡單的方法,直接修改Demo文件夾下的user_demo.mk配置文件,添加如下代碼:
#example?for?osal_task_demo ifeq?($(CONFIG_USER_DEMO),?"osal_task_demo") user_demo_src??=?${wildcard?$(TOP_DIR)/targets/STM32L431_BearPi/Demos/osal_kernel_demo/osal_task_demo.c} user_demo_defs?=?-D?CONFIG_OSAL_TASK_DEMO_ENABLE=1 endif
添加位置如圖:
這段代碼的意思是:
如果 CONFIG_USER_DEMO 宏定義的值是osal_task_demo,則將osal_task_demo.c文件加入到makefile中進行編譯。
那么,如何配置 CONFIG_USER_DEMO 宏定義呢?在工程根目錄下的.sdkconfig文件中的末尾即可配置:
因為我們修改了mk配置文件,所以點擊重新編譯按鈕進行編譯,編譯完成后點擊下載按鈕燒錄程序。
實驗現象
程序燒錄之后,即可看到程序已經開始運行,在串口終端中可看到實驗的輸出內容:
linkmain:V1.2.1?AT?11:30:59?ON?Nov?28?2019?WELCOME?TO?IOT_LINK?SHELLLiteOS:/>task?2:?my?task?id?is?5!task1:?my?task?id?is?4,?n?=?0!task?2:?my?task?id?is?5!task1:?my?task?id?is?4,?n?=?1!task?2:?my?task?id?is?5!task1:?my?task?id?is?4,?n?=?2!task?2:?my?task?id?is?5!task1:?my?task?id?is?4,?n?=?3!task?2:?my?task?id?is?5!task1:?my?task?id?is?4,?n?=?4!task?2:?my?task?id?is?5!user?task?1?exit!task?2:?my?task?id?is?5!……
可以看到,系統啟動后,首先打印版本號,串口shell的優(yōu)先級為10,最先打印shell信息,接下來task1先創(chuàng)建,但是優(yōu)先級較低,所以后創(chuàng)建的task2搶占執(zhí)行,task2打印后主動掛起2s,這時task1開始執(zhí)行,依次執(zhí)行5次后task1結束,task2一直保持運行。
評論
查看更多