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