第九章
任務函數的補充
前面講的代碼基本已經把我們的內核全部都實現完了,有一些需要有的函數沒有在前面講到,在這一章我們統一補充一下,讓我們的內核更加完善。
讓出CPU
當存在同級優先級任務的時候,任務希望把CPU讓給其它同級優先級,擔憂不想自己進入非就緒態的時候需要提供一個函數,來實現既能讓出CPU,CPU又不改變自己的就緒狀態。其實實現起來非常簡單,就是任務調度函數,但是我們的void OS_TASK_SW(void)函數是一個操作系統的私有函數,並不暴露給用戶,所以我們需要將它再封裝一個給用戶使用的讓出CPU函數。
void os_task_abandon(void)
{
OS_TASK_SW();
}
退出任務
我們正常的任務都應該是一個while 1的永不退出的循環,但是可能我們在某些條件下需要讓任務退出,或者寫一些之需要單次運行的任務。根據之前的任務切換原理我們知道,如果一個任務函數真的結束了,SP指針是無法進行切換的,這樣會讓單片機進入異常,通常會被複位。所以我們需要一個退出的函數,來告訴操作系統去調度這個任務,並且去做一些事情善後。
首先我們要把當前任務的狀態設置爲OS_STAT_DEAD
我在寫這個函數的時候就在思考,要不要做成像linux那樣的kill可以終止其它任務的樣子,後來之所以沒有做是因爲這個函數實在是太“毒”了,直接把就緒態改爲死亡態,如果能夠殺死其它任務,那麼被殺死的任務沒有機會再次運行,如果那個任務佔用了一些資源,都沒有辦法進行釋放。所以我覺得如果有這樣的功能需要實現可以使用後面的事件機制來實現異步的通知,要比這樣的殺死好的多。
void os_task_exit(void)
{
os_tcb[os_task_running_ID].OSTCBTimeQuantaCtr = 0;
os_tcb[os_task_running_ID].OSTCBStatus = OS_STAT_DEAD;
OS_TASK_SW();
}
系統資源統計
既然是操作系統,那麼就會有任務對系統資源的統計概念,如果不實現一個查看的方法好像這個系統就缺點什麼,當然了這種東西有些人可能並不會關心,所以我們參照堆棧使用的方法,做一個宏定義來控制它是否參與編譯。
#ifdef SYSTEM_DETECT_MODE
u8 *OSTCBStkBottomPtr; /* 堆棧的底部指針 */
u8 OSTCBCyclesTot; /* 當前任務運行的節拍數 */
u8 OSTCBStkSize; /* 堆棧的大小 */
#endif
我們從操作系統開始後在中斷中進行計數,每切換1次進行正在運行的任務+1,當到達100次後就整體清空,並賦值給記錄用的數組,這樣就能統計出每100次的CPU使用率。
在中斷中添加
#ifdef SYSTEM_DETECT_MODE
if (++int_count == 100) {
u8 i = 0;
os_tcb[os_task_running_ID].OSTCBCyclesTot++;
for (; i<TASK_SIZE; i++) {
sys_stat[i].OSSSStatus = os_tcb[i].OSTCBStatus;
sys_stat[i].OSSSCyclesTot = os_tcb[i].OSTCBCyclesTot;
sys_stat[i].OSSSMaxUsedStk = get_stack_used(os_tcb[i].OSTCBStkBottomPtr, os_tcb[i].OSTCBStkSize);
}
int_count = 0;
}
else {
os_tcb[os_task_running_ID].OSTCBCyclesTot++;
}
#endif
然後我們提供一個數組來存放系統查詢出來的消息
OS_SS sys_stat[TASK_SIZE]
結構體在rt_os.h中
typedef struct sys_statistics {
u8 OSSSStatus; /* 任務狀態 */
u8 OSSSCyclesTot; /* 每100個時鐘週期佔用的節拍數 */
u8 OSSSMaxUsedStk; /* 堆棧的最大使用量 */
} OS_SS;
應用程序可以自己統計該數組如下:
printf("ID\tCPU\tSTATUS\tUSED STACK\r\n");
for (i=0; i<TASK_SIZE; i++) {
if (ss[i].OSSSStatus != OS_STAT_DEFAULT) {//任務已啓動
printf("%bu\t%bu%%\t%bu\t%bu\r\n", i, ss[i].OSSSCyclesTot, ss[i].OSSSStatus, ss[i].OSSSMaxUsedStk);
}
}
下面我們修改一下任務函數,這樣可以看出統計的內容
void app_task_3(void)
{
while (1) {
printf("app_task_3\r\n");
delay_ms(20);
os_tick_sleep(100);
}
}
void app_task_1(void)
{
while (1) {
printf("app_task_1\r\n");
delay_ms(30);
os_tick_sleep(100);
}
}
void app_task_2(void)
{
#ifdef SYSTEM_DETECT_MODE
OS_SS *ss = get_sys_statistics();
#endif
while (1) {
#ifdef SYSTEM_DETECT_MODE
u8 i;
printf("ID\tCPU\tSTATUS\tUSED STACK\r\n");
for (i=0; i<TASK_SIZE; i++) {
if (ss[i].OSSSStatus != OS_STAT_DEFAULT) {//任務已啓動
printf("%bu\t%bu%%\t%bu\t%bu\r\n", i, ss[i].OSSSCyclesTot, ss[i].OSSSStatus, ss[i].OSSSMaxUsedStk);
}
}
#else
debug_print("app_task_2\r\n");
#endif
os_tick_sleep(100);
}
}
輸出如下:
[2019-09-11 19:51:11.259]# RECV ASCII>
app_task_1
[2019-09-11 19:51:11.391]# RECV ASCII>
app_task_3
[2019-09-11 19:51:11.487]# RECV ASCII>
ID CPU STATUS USED STACK
0 78% 1 19
1 13% 4 23
2 1% 4 23
3 8% 4 21
[2019-09-11 19:51:12.392]# RECV ASCII>
app_task_1
[2019-09-11 19:51:12.522]# RECV ASCII>
app_task_3
[2019-09-11 19:51:12.617]# RECV ASCII>
ID CPU STATUS USED STACK
0 78% 1 19
1 13% 4 23
2 1% 4 23
3 8% 4 21
[2019-09-11 19:51:13.523]# RECV ASCII>
app_task_1
[2019-09-11 19:51:13.652]# RECV ASCII>
app_task_3
[2019-09-11 19:51:13.750]# RECV ASCII>
ID CPU STATUS USED STACK
0 81% 2 19
1 13% 4 23
2 0% 2 23
3 6% 1 23
至此,我們的操作系統基本功能就都已經實現完成了,我們下面要在這個基礎上進行補充了。下面會分別加入任務間通訊和內存管理。