點擊上方藍色字體了解更多的嵌入式編程實用技能。
如果你覺得該文章對你有幫助,歡迎點贊+關注
在計算機科學領域,任務調度和協作是關鍵的概念。雖然傳統的操作系統提供了各種任務調度算法和機制,但有時我們需要更靈活、個性化的任務管理方式。
即使采用定時器實現時間片論法任務調度,但是也必須等單個完整的任務執行完成后才能執行下一個完整的任務。
本文將介紹使用標準庫頭文件中的setjmp和longjmp函數構建一個簡單的查詢式協作多任務系統,無需使用定時器進行任務切換。
setjmp和longjmp是C語言標準庫頭文件中提供的函數。它們的功能是實現非局部跳轉,可以在程序的不同位置之間進行跳轉,類似于goto語句的擴展。這種非局部跳轉的能力為我們構建查詢式協作多任務系統提供了基礎。
setjmp函數用于保存當前程序狀態,創建一個可以供后續longjmp函數跳轉的上下文環境。在調用setjmp時,程序會記錄當前的程序計數器、寄存器和堆棧等狀態信息,并將這些信息保存在一個jmp_buf結構中。同時,setjmp函數返回0作為普通調用的返回值,并將jmp_buf作為標識符存儲起來。
不同平臺的
jmp_buf的類型定義不一樣,大概占用不到30個字節,因為不同平臺的相關寄存器等不一樣,因此占用的大小也不同。
longjmp函數則實現了對保存的上下文環境的跳轉操作。通過傳遞之前由setjmp函數保存的jmp_buf標識符,longjmp函數會將程序的狀態還原到對應的上下文環境,并且會返回到setjmp處繼續執行。
在協作式多任務調度下,當前任務需要通過主動放棄時間片提供給其他任務運行,而并非是被其他任務搶占,因此這里面并沒有所謂的優先級概念之分。
雖然協作式沒有所謂的優先級概念之分,但是可以通過一定的方式也能實現一個簡單的優先級,比如當前任務主動放棄時間片后,查詢更高優先級的任務運行。
時間片論法任務調度只能等任務運行完成才會給下一個任務時間片運行,并不存在主動放棄時間片的功能。
了解到setjmp和longjmp的功能和原理后,我們能不能通過它們來構建一個任務調度算法和機制呢?
雖然setjmp可以記錄當前的程序計數器、寄存器和堆棧等狀態信息,但是實現多任務切換時堆棧里面的數據是會發生變化的。
jmp_buf只記錄堆棧指針,不記錄堆棧指針指向的數據內容。
因此,如果要實現多任務切換,則需要為每個任務分配一定的堆棧預留空間,由于不使用堆,因此可以只考慮棧分配即可。
當前任務主動放棄時間片后,不斷查詢滿足條件需要執行的其他任務。
創建任務,使用了setjmp函數。
int?cotOs_Creat(OsTask_cb?pfnOsTaskEnter,?size_t?stack)
{
????size_t?oldsp;
????if?(sg_OsInfo.taskNum?>=?COT_OS_MAX_TASK?||?sg_OsInfo.pfnGetTimerMs?==?NULL)
????{
????????return?-1;
????}
????COT_OS_GET_STACK(oldsp);
????COT_OS_SET_STACK(sg_OsInfo.stackTop);
????if?(0?==?setjmp(sg_OsInfo.tcb[sg_OsInfo.taskNum].env))
????{
????????COT_OS_SET_STACK(oldsp);
????????sg_OsInfo.tcb[sg_OsInfo.taskNum].pfnOsTaskEnter?=?pfnOsTaskEnter;
????????sg_OsInfo.taskNum++;
????????sg_OsInfo.stackTop?-=?stack;
????}
????else
????{
????????sg_OsInfo.tcb[sg_OsInfo.taskId].pfnOsTaskEnter();
????}
????return?0;
}
放棄時間片,使用了longjmp,這里集成了時間等待功能,即放棄時間片的時長(即使時長減至0也要等待其他任務主動放棄時間片才會運行)
void?cotOs_WaitFor(uint32_t?time)
{
????uint32_t?timer?=?sg_OsInfo.pfnGetTimerMs();
????setjmp(sg_OsInfo.tcb[sg_OsInfo.taskId].env);
????if?(!(sg_OsInfo.pfnGetTimerMs()?-?timer?>?time))
????{
????????sg_OsInfo.taskId++;
????????if?(sg_OsInfo.taskId?>=?sg_OsInfo.taskNum)
????????{
????????????sg_OsInfo.taskId?=?0;
????????}
????????longjmp(sg_OsInfo.tcb[sg_OsInfo.taskId].env,?1);
????}
}
除了繼承時間等待功能外,還定義了一個等待條件的主動放棄放棄時間片功能,即條件不滿足時主動放棄時間片(即使條件滿足后也要等待其他任務主動放棄時間片才會運行)
#define?cotOs_WaitFor_Cond(cond)???do{\
????????extern?jmp_buf?*cotOs_GetTaskEnv1(void);\
????????setjmp((*cotOs_GetTaskEnv1()));\
????????if?(!(cond)){\
????????????extern?void?cotOs_RunNextTask(void);\
????????????cotOs_RunNextTask();\
????????}\
????}while?(0)
目前已完成在STM32板子上的查詢式協作多任務系統:
下載鏈接(點擊閱讀原文):
https://gitee.com/cot_package/cot_ossetjmp和longjmp除了實現一個多任務系統外,其實還可以有其他的用法,比如實現C++中的try catch拋出異常處理功能。
由于其特性問題,在實際代碼中,特別是應用程序代碼上,建議少用
setjmp和longjmp,否則會影響代碼閱讀,當然,不排除封裝成一個特殊的功能外