Niobe開發板中基于OpenHarmony操作系統進行多線程(多任務)開發

niobe開發套件詳情介紹:??Niobe行業物聯網開發板及套件詳解??
線程的基本概念
從系統角度看,線程是競爭系統資源的最小運行單元。線程可以使用或等待CPU、使用內存空間等系統資源,并獨立于其它線程運行。
OpenHarmony LiteOS可以給用戶提供多個線程,實現線程間的切換,幫助用戶管理業務程序流程。具有如下特性:
- 支持多線程。
- 一個線程代表一個任務。
- 搶占式調度機制,高優先級的線程可打斷低優先級線程,低優先級線程必須在高優先級線程阻塞或結束后才能得到調度。
- 相同優先級線程支持時間片輪轉調度方式。
- 共有32個優先級[0-31],最高優先級為0,最低優先級為31。用戶進程可配置的優先級有22個 (10~31)。
1、線程的狀態
線程有多種運行狀態。系統初始化完成后,創建的線程就可以在系統中競爭一定的資源,由內核進行調度。
線程狀態通常分為以下四種:
- 就緒(Ready):該線程在就緒隊列中,只等待CPU。
- 運行(Running):該線程正在執行。
- 阻塞(Blocked):該線程不在就緒隊列中。包含線程被掛起(suspend狀態)、線程被延時(delay狀態)、線程正在等待信號量、讀寫隊列或者等待事件等。
- 退出態(Dead):該線程運行結束,等待系統回收資源。
2、 線程狀態遷移
就緒態→運行態: 任務創建后進入就緒態,發生任務切換時,就緒隊列中最高優先級的任務被執行,從而進入運行態,同時該任務從就緒隊列中移出。
運行態→阻塞態 :正在運行的任務發生阻塞(掛起、延時、讀信號量等)時,將該任務插入到對應的阻塞隊列中,任務狀態由運行態變成阻塞態,然后發生任務切換,運行就緒隊列中最高優先級任務。
阻塞態→就緒態(阻塞態→運行態):阻塞的任務被恢復后(任務恢復、延時時間超時、讀信號量超時或讀到信號量等),此時被恢復的任務會被加入就緒隊列,從而由阻塞態變成就緒態;此時如果被恢復任務的優先級高于正在運行任務的優先級,則會發生任務切換,該任務由就緒態變成運行態。
就緒態→阻塞態 : 任務也有可能在就緒態時被阻塞(掛起),此時任務狀態由就緒態變為阻塞態,該任務從就緒隊列中刪除,不會參與任務調度,直到該任務被恢復。
運行態→就緒態 : 有更高優先級任務創建或者恢復后,會發生任務調度,此刻就緒隊列中最高優先級任務變為運行態,那么原先運行的任務由運行態變為就緒態,依然在就緒隊列中。
運行態→退出態 : 運行中的任務運行結束,任務狀態由運行態變為退出態。退出態包含任務運行結束的正常退出狀態以及Invalid狀態。例如,任務運行結束但是沒有自刪除,對外呈現的就是Invalid狀態,即退出態。
阻塞態→退出態 : 阻塞的任務調用刪除接口,任務狀態由阻塞態變為退出態。
3、線程管理
對于多線程的場景,HarmonyOS內核管理線程靠任務池和就緒隊列,執行靠調度算法。
調度算法:HarmonyOS內核中的線程采用搶占式調度機制,同時支持SCHED_RR和SCHED_FIFO調度策略


RR策略能基本保證我們每個任務都能夠得到有效的執行,不會有一些任務進行長時間等待
FIFO策略優點在于任務的切換比較簡單,而且對于一些時間片不好把握的任務來說,FIFO能偶更有效的利用我們的cpu。
線程相關API
此處介紹cmsis2.0的線程接口,頭文件:”//third_party/cmsis/CMSIS/RTOS2/Include/cmsis_os2.h”

創建線程
osThreadId_t osThreadNew (osThreadFunc_t func, void *argument, const osThreadAttr_t *attr);
函數osThreadNew通過將線程添加到活動線程列表并將其設置為就緒狀態來啟動線程函數。線程函數的參數使用參數指針*argument傳遞。當創建的thread函數的優先級高于當前運行的線程時,創建的thread函數立即啟動并成為新的運行線程。線程屬性是用參數指針attr定義的。屬性包括線程優先級、堆棧大小或內存分配的設置。可以在RTOS啟動(調用 osKernelStart)之前安全地調用該函數,但不能在內核初始化 (調用 osKernelInitialize)之前調用該函數。


開發實例
1、 確定目錄結構
先在路徑./applications/app下新建一個目錄,用于存放業務源碼文件。其中“.”表示OpenHarmony源碼的根目錄。
例如:在app下新增業務NIOBE_OS_helloworld,其中hello_world.c為業務代碼,BUILD.gn為編譯腳本,其目錄結構如下:
.
└── applications
└── app
│── TW002_OS_thread
│ │── os_thread_example.c
│ └── BUILD.gn
└── BUILD.gn
2、 編寫業務代碼
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
/*****任務一*****/
void thread_entry1(void)
{
int sum = 0;
while (1)
{
printf("This is Niobe Thread1----%d\r\n", sum++);
usleep(500000);
}
}
/*****任務二*****/
void thread_entry2(void)
{
int sum = 0;
while (1)
{
printf("This is Niobe Thread2----%d\r\n", sum++);
usleep(500000);
}
}
/*****任務創建*****/
static void OS_Thread_example(void)
{
osThreadAttr_t attr;
attr.name = "thread1";
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = 1024 * 4;
attr.priority = 25;
if (osThreadNew((osThreadFunc_t)thread_entry1, NULL, &attr) == NULL)
{
printf("Falied to create thread1!\n");
}
attr.name = "thread2";
if (osThreadNew((osThreadFunc_t)thread_entry2, NULL, &attr) == NULL)
{
printf("Falied to create thread2!\n");
}
}
3、 編寫將業務構建成靜態庫的BUILD.gn
static_library("os_thread_example"){
sources = [
"os_thread_example.c"
]
include_dirs = [
"http://third_party/cmsis/CMSIS/RTOS2/Include"
]
}
4、編寫模塊BUILD.gn文件
import("http://build/lite/config/component/lite_component.gni")
lite_component("app") {
features = [
#"NIOBE_OS_helloworld:helloworld",
"TW002_OS_thread:os_thread_example"
]
}
編譯
用docker編譯,進入OpenHarmony代碼根目錄,運行命令進入docker鏡像,在鏡像中用hb編譯:
sudo docker run -it -v $(pwd):/home/openharmony swr.cn-south-1.myhuaweicloud.com/openharmony-docker/openharmony-docker:0.0.5
hb set
.
//繼續回車選擇niobe_wifi_iot
hb build -b release -f

等待編譯成功。

燒錄
編譯成功后,bin文件會保存在out/niobe/niobe_wifi_iot目錄下:

用HiBurn.exe將Hi3861_wifiiot_app_allinone.bin文件燒錄到niobe核心板上:
首先用typeC線連接電腦和Niobe核心板,可通過設備管理確定Niobe連接的端口號,該端口號后續HiBurn和sscom都需要。

再通過HiBurn.exe工具將固件燒錄到Niobe上,HiBurn工具的獲取和操作可參考燒錄指導
調試
采用串口調試工具sscom查看串口打印信息,先對sscom進行配置,設置端口號、波特率等:

點擊打開串口,按下Niobe核心板上的復位按鍵,可通過sscom看到串口打印日志如下:
This is Niobe Thread1----2
This is Niobe Thread2----5
This is Niobe Thread1----3
This is Niobe Thread2----6
可以看到線程thread_entry1和線程thread_entry2交替運行。





























