本次內(nèi)容主要以Huawei LiteOS為主,講解Huawei LiteOS內(nèi)核所提供任務(wù)的創(chuàng)建展父、刪除返劲、延遲、掛起栖茉、恢復(fù)等功能篮绿,以及鎖定和解鎖任務(wù)調(diào)度,支持任務(wù)按優(yōu)先級高低的搶占調(diào)度及同優(yōu)先級時(shí)間片輪轉(zhuǎn)調(diào)度吕漂。在 LiteOS 中亲配,一個(gè)任務(wù)就是一個(gè)線程,多個(gè)任務(wù)按照優(yōu)先級進(jìn)行搶占式調(diào)度,達(dá)到多個(gè)任務(wù)“同時(shí)”運(yùn)行的目的弃榨。Huawei LiteOS系統(tǒng)中的每個(gè)任務(wù)都有多種運(yùn)行狀態(tài)菩收,當(dāng)系統(tǒng)初始化完成并啟動調(diào)度器后,系統(tǒng)中所有創(chuàng)建的任務(wù)就由內(nèi)核進(jìn)行調(diào)度鲸睛,在不同運(yùn)行狀態(tài)之間切換娜饵,同時(shí)在系統(tǒng)中競爭一定的資源。
任務(wù)的狀態(tài)有以下四種:
①就緒(Ready):該任務(wù)在就緒列表中官辈,只等待 CPU箱舞;
②運(yùn)行(Running):該任務(wù)正在執(zhí)行;
③阻塞(Blocked):該任務(wù)不在就緒列表中拳亿。包含任務(wù)被掛起晴股、任務(wù)被延時(shí)、任務(wù)正在等待信號量肺魁、讀寫隊(duì)列或者等待讀寫事件等电湘;
④退出態(tài)(Dead):該任務(wù)運(yùn)行結(jié)束,等待系統(tǒng)回收資源鹅经。
下面我們將進(jìn)入Huawei LiteOS實(shí)驗(yàn)的具體講解:
一寂呛、LiteOS內(nèi)核的任務(wù)管理
本次實(shí)驗(yàn)內(nèi)容為:創(chuàng)建兩個(gè)任務(wù),一個(gè)低優(yōu)先級任務(wù)task1瘾晃,一個(gè)高優(yōu)先級任務(wù)task2贷痪,兩個(gè)任務(wù)都會每隔2s在串口打印自己的任務(wù)id號,在串口終端中觀察兩個(gè)任務(wù)的運(yùn)行情況蹦误。
1.點(diǎn)擊進(jìn)入IoT studio軟件劫拢,并打開之前所創(chuàng)建的 HelloWorld 工程,基于此工程進(jìn)行實(shí)驗(yàn)强胰。
2.進(jìn)入 HelloWorld 工程后在資源管理器中找到Demo文件夾舱沧,右擊Demo文件夾,選擇新建文件夾
點(diǎn)擊創(chuàng)建osal_kernel_demo文件夾
3.接下來在osal_kernel_demo文件夾中熟吏,新建第1個(gè)實(shí)驗(yàn)文件osal_task_demo.c文件,并開始編寫代碼:
/* 使用osal接口需要包含該頭文件 */
#include <osal.h>
/* 任務(wù)優(yōu)先級宏定義(shell任務(wù)的優(yōu)先級為10) */
#define USER_TASK1_PRI 12 //低優(yōu)先級
#define USER_TASK2_PRI 11 //高優(yōu)先級
/* 任務(wù)ID */
uint32_t user_task1_id = 0;
uint32_t user_task2_id = 0;
/* 任務(wù)task1入口函數(shù) */
static int user_task1_entry()
{
int n = 0;
/* 每隔2s在串口打印一次,打印5次后主動結(jié)束 */
for(n = 0; n < 5; n++)
{
printf("task1: my task id is %ld, n = %d!\r\n", user_task1_id, n);
/* 任務(wù)主動掛起2s */
osal_task_sleep(2*1000);
}
printf("user task 1 exit!\r\n");
/* 任務(wù)結(jié)束 */
return 0;
}
/* 任務(wù)task2入口函數(shù) */
static int user_task2_entry()
{
/* 每隔2s在串口打印一次肾筐,不結(jié)束 */
while (1)
{
printf("task 2: my task id is %ld!\r\n", user_task2_id);
/* 任務(wù)主動掛起2s */
osal_task_sleep(2*1000);
}
}
/* 標(biāo)準(zhǔn)demo啟動函數(shù)哆料,函數(shù)名不要修改,否則會影響下一步實(shí)驗(yàn) */
int standard_app_demo_main()
{
/* 創(chuàng)建任務(wù)task1 */
user_task1_id = osal_task_create("user_task1",user_task1_entry,NULL,0x400,NULL,USER_TASK1_PRI);
/* 創(chuàng)建任務(wù)task2 */
user_task2_id = osal_task_create("user_task2",user_task2_entry,NULL,0x400,NULL,USER_TASK2_PRI);
return 0;
4.編寫完成之后吗铐,將我們所編寫的osal_task_demo.c文件添加到makefile中东亦,并加入整個(gè)工程的編譯,也可以直接修改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
5.再將osal_task_demo.c文件加入makefile中進(jìn)行編譯。并在資源管理器下的.sdkconfig文件末尾進(jìn)行配置:
CONFIG_USER_DEMO = "osal_task_demo"
6.由于我們修改了mk配置文件典阵,所以需點(diǎn)擊重新編譯按鈕(即①)奋渔,編譯完成后再點(diǎn)擊下載按鈕(即②)燒錄程序。
7.程序燒錄完成后壮啊,即可看到程序已經(jīng)開始運(yùn)行嫉鲸,在工程-工程配置-串口配置中,可對串口進(jìn)行配置
8.串口配置完成后歹啼,可在串口終端中可看到實(shí)驗(yàn)的輸出內(nèi)容:
9.總結(jié):在系統(tǒng)啟動后玄渗,首先打印版本號,串口shell的優(yōu)先級為10狸眼,最先打印shell信息藤树,接下來task1先創(chuàng)建,但是優(yōu)先級較低拓萌,所以后創(chuàng)建的task2搶占執(zhí)行岁钓,task2打印后主動掛起2s,這時(shí)task1開始執(zhí)行微王,依次執(zhí)行5次后task1結(jié)束屡限,task2一直保持運(yùn)行。
二骂远、 LiteOS的互斥鎖
關(guān)于互斥鎖的使用方式
多任務(wù)環(huán)境下會存在多個(gè)任務(wù)訪問同一公共資源的場景囚霸,而有些公共資源是非共享的,需要任務(wù)進(jìn)行獨(dú)占式處理激才。在任意時(shí)刻拓型,互斥鎖的狀態(tài)只有兩種:開鎖和閉鎖。
當(dāng)有任務(wù)持有時(shí)瘸恼,互斥鎖處于閉鎖狀態(tài)劣挫,這個(gè)任務(wù)獲得該互斥鎖的所有權(quán)。當(dāng)該任務(wù)釋放它時(shí)东帅,該互斥鎖被開鎖压固,任務(wù)失去該互斥鎖的所有權(quán)。當(dāng)一個(gè)任務(wù)持有互斥鎖時(shí)靠闭,其他任務(wù)將不能再對該互斥鎖進(jìn)行開鎖或持有帐我。當(dāng)一個(gè)互斥鎖為加鎖狀態(tài)時(shí),此時(shí)其他任務(wù)如果想訪問這個(gè)公共資源則會被阻塞愧膀,直到互斥鎖被持有該鎖的任務(wù)釋放后拦键,其他任務(wù)才能重新訪問該公共資源,此時(shí)互斥鎖再次上鎖檩淋,如此確保同一時(shí)刻只有一個(gè)任務(wù)正在訪問這個(gè)公共資源芬为,保證了公共資源操作的完整性。
本次實(shí)驗(yàn)內(nèi)容為:創(chuàng)建兩個(gè)任務(wù),一個(gè)低優(yōu)先級任務(wù)task1媚朦,一個(gè)高優(yōu)先級任務(wù)task2氧敢,兩個(gè)任務(wù)之間依次對共享資源上鎖、操作询张、解鎖孙乖,在串口終端中觀察兩個(gè)任務(wù)的運(yùn)行情況。
1.點(diǎn)擊進(jìn)入IoT studio軟件瑞侮,并打開之前所創(chuàng)建的 HelloWorld 工程的圆,基于此工程進(jìn)行實(shí)驗(yàn)。
2.進(jìn)入 HelloWorld 工程后在資源管理器中找到Demo文件夾半火,右擊Demo文件夾越妈,
點(diǎn)擊新建文件夾osal_kernel_demo用于存放內(nèi)核的實(shí)驗(yàn)文件(如果已有請忽略這一步)制轰。
點(diǎn)擊創(chuàng)建osal_kernel_demo文件夾
3.在osal_kernel_demo文件夾中新建一個(gè)實(shí)驗(yàn)文件 osal_mutex_demo.c,開始編寫代碼:
于 osal_mutex_demo.c文件中添加下列代碼:
/* 使用osal接口需要包含該頭文件 */
#include <osal.h>
/* 任務(wù)優(yōu)先級宏定義(shell任務(wù)的優(yōu)先級為10) */
#define USER_TASK1_PRI 12 //低優(yōu)先級
#define USER_TASK2_PRI 11 //高優(yōu)先級
/* 共享資源 */
uint32_t public_value = 0;
/* 互斥鎖索引ID */
osal_mutex_t public_value_mutex;
/* 任務(wù)task1入口函數(shù) */
static int user_task1_entry()
{
while(1)
{
/* 嘗試獲取互斥鎖 */
if(true == osal_mutex_lock(public_value_mutex))
{
/* 獲取到互斥鎖玉掸,對共享資源進(jìn)行操作 */
printf("\r\ntask1: lock a mutex.\r\n");
public_value += 10;
printf("task1: public_value = %ld.\r\n", public_value);
/* 對共享資源操作完畢店归,釋放互斥鎖 */
printf("task1: unlock a mutex.\r\n\r\n");
osal_mutex_unlock(public_value_mutex);
/* 滿足條件則結(jié)束任務(wù) */
if(public_value > 100)
break;
}
}
/* while(1)會執(zhí)行結(jié)束阎抒,所以需要返回值 */
return 0;
}
/* 任務(wù)task2入口函數(shù) */
static int user_task2_entry()
{
while (1)
{
/* 嘗試獲取互斥鎖 */
if(true == osal_mutex_lock(public_value_mutex))
{
/* 獲取到互斥鎖,對共享資源進(jìn)行操作 */
printf("\r\ntask2: lock a mutex.\r\n");
public_value += 5;
printf("task2: public_value = %ld.\r\n", public_value);
/* 對共享資源操作完畢消痛,釋放互斥鎖 */
printf("task2: unlock a mutex.\r\n\r\n");
osal_mutex_unlock(public_value_mutex);
/* 滿足條件則結(jié)束任務(wù) */
if(public_value > 90)
break;
/* 優(yōu)先級較高且叁,需要掛起一下,讓task1獲取到互斥鎖秩伞,否則task2再次上鎖逞带,形成死鎖 */
osal_task_sleep(10);
}
}
/* while(1)會執(zhí)行結(jié)束,所以需要返回值 */
return 0;
}
/* 標(biāo)準(zhǔn)demo啟動函數(shù)纱新,函數(shù)名不要修改展氓,否則會影響下一步實(shí)驗(yàn) */
int standard_app_demo_main()
{
/* 創(chuàng)建互斥鎖public_value_mutex */
osal_mutex_create(&public_value_mutex);
/* 創(chuàng)建任務(wù)task1 */
osal_task_create("user_task1",user_task1_entry,NULL,0x400,NULL,USER_TASK1_PRI);
/* 創(chuàng)建任務(wù)task2 */
osal_task_create("user_task2",user_task2_entry,NULL,0x400,NULL,USER_TASK2_PRI);
return 0;
}
4.編寫完成之后,將我們所編寫的osal_mutex_demo.c文件添加到makefile中脸爱,并加入整個(gè)工程的編譯,也可以直接修改Demo文件夾下的user_demo.mk配置文件遇汞,添加如下代碼:
#example for osal_mutex_demo
ifeq ($(CONFIG_USER_DEMO), "osal_mutex_demo")
user_demo_src = ${wildcard $(TOP_DIR)/targets/STM32L431_BearPi/Demos/osal_kernel_demo/osal_mutex_demo.c}
endif
添加位置如圖所示:
5.再將osal_mutex_demo.c文件加入makefile中進(jìn)行編譯。并在資源管理器下的.sdkconfig文件末尾進(jìn)行配置:
CONFIG_USER_DEMO = "osal_mutex_demo"
配置如圖所示:
6.由于我們修改了mk配置文件簿废,所以需點(diǎn)擊重新編譯按鈕(即①)空入,編譯完成后再點(diǎn)擊下載按鈕(即②)燒錄程序。
7.程序燒錄完成后族檬,即可看到程序已經(jīng)開始運(yùn)行歪赢,在工程-工程配置-串口配置中,可對串口進(jìn)行配置
8.串口配置完成后导梆,可在串口終端中可看到實(shí)驗(yàn)的輸出內(nèi)容:
9.總結(jié):在系統(tǒng)啟動后轨淌,首先打印版本號,串口shell的優(yōu)先級為10看尼,最先打印shell信息递鹉,接下來task1先創(chuàng)建,但是優(yōu)先級較低藏斩,所以后創(chuàng)建的task2搶占執(zhí)行躏结,task2獲取到互斥鎖,對共享資源進(jìn)行操作狰域,操作完畢解鎖媳拴,然后主動掛起,task1獲取到互斥鎖兆览,對共享資源進(jìn)行另一個(gè)操作屈溉,操作完畢解鎖,在task1操作的時(shí)候抬探,task2早已掛起完畢子巾,但是獲取不到互斥鎖,所以掛起等待小压,在task1解鎖后线梗,堵塞的task2被喚醒開始執(zhí)行。
三怠益、 LiteOS的內(nèi)存管理
關(guān)于內(nèi)存管理
在系統(tǒng)運(yùn)行的過程中仪搔,一些內(nèi)存空間大小是不確定的,比如一些數(shù)據(jù)緩沖區(qū)蜻牢,所以系統(tǒng)需要提供內(nèi)存空間的管理能力烤咧,用戶可以在使用的時(shí)候申請需要的內(nèi)存空間,使用完畢釋放該空間孩饼,以便再次利用髓削。Huawei LiteOS 的內(nèi)存管理模塊通過對內(nèi)存的申請/釋放操作,來管理用戶和OS對內(nèi)存的使用镀娶,使內(nèi)存的利用率和使用效率達(dá)到最優(yōu)立膛,同時(shí)最大限度地解決系統(tǒng)的內(nèi)存碎片問題。
動態(tài)內(nèi)存管理梯码,即在內(nèi)存資源充足的情況下宝泵,從系統(tǒng)配置的一塊比較大的連續(xù)內(nèi)存(內(nèi)存池),根據(jù)用戶需求轩娶,分配任意大小的內(nèi)存塊儿奶。當(dāng)用戶不需要該內(nèi)存塊時(shí),又可以釋放回系統(tǒng)供下一次使用鳄抒。與靜態(tài)內(nèi)存相比闯捎,動態(tài)內(nèi)存管理的好處是按需分配椰弊,缺點(diǎn)是內(nèi)存池中容易出現(xiàn)碎片。
LiteOS動態(tài)內(nèi)存支持 DLINK 和 BEST LITTLE 兩種標(biāo)準(zhǔn)算法瓤鼻。
本次實(shí)驗(yàn)內(nèi)容為:創(chuàng)建一個(gè)任務(wù)秉版,從最小字節(jié)開始,不停的申請分配內(nèi)存茬祷,釋放分配的內(nèi)存清焕,直到申請失敗,串口終端中觀察可以申請到的最大字節(jié)祭犯。
1.點(diǎn)擊進(jìn)入IoT studio軟件秸妥,并打開之前所創(chuàng)建的 HelloWorld 工程,基于此工程進(jìn)行實(shí)驗(yàn)沃粗。
2.進(jìn)入 HelloWorld 工程后在資源管理器中找到Demo文件夾粥惧,右擊Demo文件夾,選擇新建文件夾
點(diǎn)擊創(chuàng)建osal_kernel_demo文件夾
3.接下來在osal_kernel_demo文件夾中影晓,新建第1個(gè)實(shí)驗(yàn)文件osal_mem_demo.c文件,并開始編寫代碼:
/* 使用osal接口需要包含該頭文件 */
#include <osal.h>
/* 任務(wù)優(yōu)先級宏定義(shell任務(wù)的優(yōu)先級為10) */
#define USER_TASK1_PRI 12 //低優(yōu)先級
#define USER_TASK2_PRI 11 //高優(yōu)先級
/* 任務(wù)ID */
uint32_t user_task1_id = 0;
uint32_t user_task2_id = 0;
/* 任務(wù)task1入口函數(shù) */
static int user_task1_entry()
{
int n = 0;
/* 每隔2s在串口打印一次檩禾,打印5次后主動結(jié)束 */
for(n = 0; n < 5; n++)
{
printf("task1: my task id is %ld, n = %d!\r\n", user_task1_id, n);
/* 任務(wù)主動掛起2s */
osal_task_sleep(2*1000);
}
printf("user task 1 exit!\r\n");
/* 任務(wù)結(jié)束 */
return 0;
}
/* 任務(wù)task2入口函數(shù) */
static int user_task2_entry()
{
/* 每隔2s在串口打印一次挂签,不結(jié)束 */
while (1)
{
printf("task 2: my task id is %ld!\r\n", user_task2_id);
/* 任務(wù)主動掛起2s */
osal_task_sleep(2*1000);
}
}
/* 標(biāo)準(zhǔn)demo啟動函數(shù),函數(shù)名不要修改盼产,否則會影響下一步實(shí)驗(yàn) */
int standard_app_demo_main()
{
/* 創(chuàng)建任務(wù)task1 */
user_task1_id = osal_task_create("user_task1",user_task1_entry,NULL,0x400,NULL,USER_TASK1_PRI);
/* 創(chuàng)建任務(wù)task2 */
user_task2_id = osal_task_create("user_task2",user_task2_entry,NULL,0x400,NULL,USER_TASK2_PRI);
return 0;
4.編寫完成之后饵婆,將我們所編寫的osal_mem_demo.c文件添加到makefile中,并加入整個(gè)工程的編譯,也可以直接修改Demo文件夾下的user_demo.mk配置文件戏售,添加如下代碼:
#example for osal_mem_demo
ifeq ($(CONFIG_USER_DEMO), "osal_mem_demo")
user_demo_src = ${wildcard $(TOP_DIR)/targets/STM32L431_BearPi/Demos/osal_kernel_demo/osal_mem_demo.c}
endif
5.再將osal_mem_demo.c文件加入makefile中進(jìn)行編譯侨核。并在資源管理器下的.sdkconfig文件末尾進(jìn)行配置:
CONFIG_USER_DEMO = "osal_men_demo"