使用了標準庫頭文件 <setjmp.h>中的 setjmp 和 longjmp兩個函數,構建了一個簡單的查詢式協作多任務系統,支持獨立棧和共用棧兩種任務。 其中涉及到獲取和設置棧的地址操作,因此還需要根據不同平臺提供獲取和設置棧的地址操作(一般是彙編語言,因為涉及到寄存器) 該調度系統僅運行在一個實 ...
使用了標準庫頭文件 <setjmp.h>
中的 setjmp
和 longjmp
兩個函數,構建了一個簡單的查詢式協作多任務系統,支持獨立棧和共用棧兩種任務。
- 其中涉及到獲取和設置棧的地址操作,因此還需要根據不同平臺提供獲取和設置棧的地址操作(一般是彙編語言,因為涉及到寄存器)
- 該調度系統僅運行在一個實際的線程中,因此本質上屬於協程
- 獨立棧任務都有自己獨立的運行棧空間,互不幹擾;共用棧任務共用一個運行棧空間。
特點
-
無任務優先順序搶占的功能。
-
任務切換的時機完全取決於正在運行的任務,體現協作。
-
支持獨立棧和共用棧兩種任務,根據不同的應用場景決定。
-
查詢式的調度方式,當前任務切換時,查詢下個任務是否需要執行。
-
移植性強,只需要修改設置棧和獲取當前棧地址的巨集即可。
-
相對於時間片論法的任務調度來說,查詢式協作多任務系統有以下特點:
- 無需使用定時器做為任務調度
- 每個任務都可以使用
while
迴圈,用於執行任務並保持程式的運行,程式結構清晰 - 每個任務都可以隨時阻塞等待,甚至可以在嵌套的子函數中阻塞等待
- 通過阻塞等待,無需使用狀態機等較為複雜的方式來優化縮減每個任務的執行時長
-
相對於RTOS操作系統來說,查詢式協作多任務系統有以下特點:
- 沒有任務優先順序搶占式的功能,因此臨界資源(中斷除外)和優先順序反轉的問題也不存在
- 允許用戶或應用程式根據需要自由地切換到下一個就緒任務
- 通過自主調度和管理任務,查詢式協作多任務系統可以提高工作效率
- 沒有操作系統的複雜
功能設計
運行棧空間:程式運行中發生函數調用等情況需要使用的棧記憶體空間
獨立棧任務(有棧任務)
每個獨立棧任務都擁有自己獨立的運行棧空間,可以隨時隨地阻塞等待,保存上下文後切換到下一個任務執行
獨立棧任務在切換下一個任務時,不會操作運行棧,只對上下文切換
共用棧任務(無棧任務)
每個共用棧任務都沒有自己獨立的運行棧空間,雖然也能阻塞等待,但是僅限於在任務入口函數中使用,禁止在任務的子函數(嵌套函數)中阻塞等待;並且在該任務入口函數中不建議定義相關變數。
- 每個任務有自己的獨立備份棧(用來備份運行棧的棧頂部分數據);運行棧通常比備份棧要大很多,否則任務函數無法正常運行多級嵌套的函數
- 共用棧任務在切換下一個任務時會將當前運行棧(共用棧)提前設置好的備份棧大小(巨集配置)拷貝到記憶體備份起來,等下次即將執行時再從記憶體中拷貝到運行棧(共用棧)進行恢復
- 通過修改加大備份棧大小(巨集配置)的值,可以在共用棧任務入口函數定義變數,這樣可以避免這些變數的值沒有備份導致丟失,或者通過 static 定義局部變數
- 該類型任務適合於輕量的任務處理,一般都是調用封裝好的函數即可
註:這裡的共用棧任務和常規的實現有一些差異,常規的實現是使用堆申請記憶體保存棧的數據,用多少申請多少進行保存,而這裡的實現僅僅保存了一部分數據。
任務創建
- 在調度系統啟動前,至少要先創建一個任務,否則直接退出
- 可以在任務中創建新的任務,不管是獨立棧任務還是共用棧任務
- 獨立棧任務中可以創建新的獨立棧任務和共用棧任務
- 共用棧任務中同樣可以創建新的獨立棧任務和共用棧任務,而且在創建共用棧任務時可以使用同一個共用棧
- 獨立棧任務和共用棧任務一共可以創建最多32個任務(需要修改巨集配置)
任務銷毀
- 沒有提供該功能介面函數,任務入口函數主動退出則自動將任務銷毀。
- 可以通過等待任務退出介面函數在其他任務中等待該任務退出。
任務阻塞
當前任務阻塞提供兩種方式:
- 時間阻塞:需要阻塞多長時間,等時間滿足後才會繼續執行
- 事件阻塞:通過事件阻塞,只有事件觸發後才會繼續執行
使用說明
任務創建/退出
對於創建獨立棧任務還是共用棧任務的示例代碼:
uint8_t g_task1Stack[1024 * 2];
uint8_t g_task2Stack[1024 * 2];
uint8_t g_task3Stack[1024 * 2];
uint8_t g_sharedStack[1024 * 2];
// 執行完成就退出的任務
void taskfunc3(int arg)
{
...
cotOs_Wait(1000);
...
cotOs_Wait(1000);
}
void taskfunc1(int arg)
{
/* 不管taskfunc1是獨立棧任務還是共用棧任務,都支持創建子任務 */
cotOs_CreatTask(taskfunc3, COT_OS_UNIQUE_STACK, g_task3Stack, sizeof(g_task3Stack), 0); // 創建獨立棧任務
cotOs_CreatTask(taskfunc3, COT_OS_SHARED_STACK, g_sharedStack, sizeof(g_sharedStack), 0); // 創建共用棧任務
while (1)
{
...
cotOs_Wait(1000);
}
}
void taskfunc2(int arg)
{
while (1)
{
...
cotOs_Wait(10);
}
}
int main(void)
{
cotOs_Init(GetTimerMs);
#if 0
/* 創建獨立棧任務 */
cotOs_CreatTask(taskfunc1, COT_OS_UNIQUE_STACK, g_task1Stack, sizeof(g_task1Stack), 0);
cotOs_CreatTask(taskfunc2, COT_OS_UNIQUE_STACK, g_task2Stack, sizeof(g_task2Stack), 0);
#else
/* 創建共用棧任務 */
cotOs_CreatTask(taskfunc1, COT_OS_SHARED_STACK, g_sharedStack, sizeof(g_sharedStack), 0);
cotOs_CreatTask(taskfunc2, COT_OS_SHARED_STACK, g_sharedStack, sizeof(g_sharedStack), 0);
#endif
cotOs_Start();
}
任務限制
對於創建獨立棧任務還是共用棧任務,共用棧任務有限制要求,禁止在任務入口函數的嵌套函數中阻塞
uint8_t g_task1Stack[1024 * 2];
uint8_t g_sharedStack[1024 * 2];
void func1_1(void)
{
...
cotOs_Wait(1000);
...
cotOs_Wait(1000);
}
/* 獨立棧任務 */
void taskfunc1(int arg)
{
int arr[10]; // 可以直接定義變數使用
while (1)
{
func1_1(); // 可以在嵌套函數中使用阻塞等待
...
cotOs_Wait(1000);
}
}
void func2_1(void)
{
...
}
/* 共用棧任務 */
void taskfunc2(int arg)
{
static int arr[10]; // 建議使用static定義任務內變數或者不定義變數
while (1)
{
func2_1(); // 禁止在嵌套函數中使用阻塞等待
...
cotOs_Wait(10);
}
}
int main(void)
{
cotOs_Init(GetTimerMs);
/* 創建獨立棧任務 */
cotOs_CreatTask(taskfunc1, COT_OS_UNIQUE_STACK, g_task1Stack, sizeof(g_task1Stack), 0);
/* 創建共用棧任務 */
cotOs_CreatTask(taskfunc2, COT_OS_SHARED_STACK, g_sharedStack, sizeof(g_sharedStack), 0);
cotOs_Start();
}
任務阻塞/退出
通過時間和事件的方式阻塞
uint8_t g_task1Stack[1024 * 2];
uint8_t g_task2Stack[1024 * 2];
uint8_t g_task3Stack[1024 * 2];
uint8_t g_sharedStack[1024 * 2];
CotOSCondition_t g_eventCv;
// 執行完成就退出的任務
void taskfunc3(int arg)
{
...
cotOs_ConditionWait(&g_eventCv);
...
}
void taskfunc1(int arg)
{
cotOsTask_t task = cotOs_CreatTask(taskfunc3, COT_OS_UNIQUE_STACK, g_task3Stack, sizeof(g_task3Stack), 0);
while (1)
{
...
cotOs_Wait(1000);
if (...)
{
// 等待 taskfunc3 任務運行結束後才退出 taskfunc1
cotOs_Join(task);
break;
}
}
}
void taskfunc2(int arg)
{
while (1)
{
...
cotOs_Wait(10);
if (...)
{
cotOs_ConditionNotify(&g_eventCv); // 通知 taskfunc3 繼續執行
}
}
}
int main(void)
{
cotOs_Init(GetTimerMs);
cotOs_CreatTask(taskfunc1, COT_OS_SHARED_STACK, g_sharedStack, sizeof(g_sharedStack), 0);
cotOs_CreatTask(taskfunc2, COT_OS_SHARED_STACK, g_sharedStack, sizeof(g_sharedStack), 0);
cotOs_Start();
}
不同棧類型任務應用場景
-
獨立棧任務(有棧任務)
- 重量級任務: 提供更多的控制,適用於需要更精確地管理任務狀態的情況和執行計算密集型任務的場景
- 更可預測的記憶體使用: 在創建時分配棧空間,可以更好地控制記憶體使用,適用於需要更可預測記憶體行為的場景
- 遞歸調用: 更容易處理遞歸調用,因為每個任務都有獨立的棧空間
-
共用棧任務(無棧任務)
- 輕量級任務: 通常更輕量,適用於大量小任務的場景。
- 記憶體效率: 適用於記憶體受限的環境,因為不需要為每個任務分配各自的棧空間(備份棧除外)。
代碼鏈接
本文來自博客園,作者:大橙子瘋,轉載請註明原文鏈接:https://www.cnblogs.com/const-zpc/p/18007643