本模塊介紹SYS/BIOS中的第三種線程:TASK線程。
Task線程被Task模塊所管理控制,Task的優先級高於空閒循環(Idle Loop),但是低於硬件中斷和軟件中斷。Task模塊基於Task的優先級和當前執行的狀態動態地調度和搶佔任務。這樣確保了處理器始終準備運行最高優先級的線程。Task線程擁有高達32個優先等級。默認爲16,最低優先級爲0,即空閒循環。
動態創建和刪除Task:
Task_Params taskParams;
Task_Handle task0;
Error_Block eb;
Error_init(&eb);
/* Create 1 task with priority 15 */
Task_Params_init(&taskParams);
taskParams.stackSize = 512;
taskParams.priority = 15;
task0 = Task_create((Task_FuncPtr)hiPriTask, &taskParams, &eb);
if (task0 == NULL) {
System_abort("Task create failed");
}
以上代碼顯示瞭如何動態創建一個Task,通過調用Task_create()函數,來派生SYS/BIOS任務,其參數包括新任務開始執行的C函數地址, Task_create()返回值爲一個任務句柄,可以作爲一個變量傳遞個其他任務函數。
如果傳遞NULL,而不是指向實際的task_Params結構體指針,會使用默認的參數,eb是一個錯誤塊,可以處理在任務對象創建期間可能產生的錯誤。
通過使用Task_delete函數來回收任務對象和堆棧使用的存儲空間,並釋放任務對象的堆棧。但是該函數不能釋放被任務所擁有的Semaphore或其他資源。
靜態創建Task:
使用Task_create()可以靜態創建任務,但是不能使用Task_delete來刪除靜態創建的任務,
Task線程模塊會自動創建Task_idle任務線程並且給予最低的優先級。當沒有更高優先級的HWI,SWI,TASK運行時,會執行Task_idle。如果想讓Task線程初始不被激活,可以設置起優先級爲-1。只有當優先級上上升時纔會被激活。
每個任務對象總是處於以下四個可能的執行狀態之一:
- Task_Mode_RUNNING:任務線程正在系統中執行
- Task_Mode_READY:任務已經準備好用於編排執行,是否執行取決於處理器
- Task_Mode_BLOCKED:任務線程不能被執行,直到特定的時間在系統中發生
- Task_Mode_TERMINATED:任務線程被終止了且沒有再執行
- Task_Mode_INACTIVE:任務有一個-1的優先級,並且處於準備好的狀態,可以在運行時調用Task_setPri()來設置優先級或者創建任務來改變優先級
Task線程的最大優先級爲Task_numPriorities-1 (default=15; maximum=31).如果達到最大優先級,那麼該線程就不能被其他任務線程搶佔,最高優先級的Task線程仍然可以調用Semaphore_pend(), Task_sleep()或者其他低優先級任務線程執行阻塞調用.Task線程可以在運行時通過Task_setPr()來改變優先級.
上圖展示了任務線程執行模式的改變.
一個Task線程中只有一個Task_Mode_RUNNING執行模式,如果所有的程序任務被阻塞或者沒有HWI,SWI在運行,那麼Task線程會執行Task_idle線程,該優先級低於系統中所有其他任務線程的優先級.當一個任務線程被HWI,SWI所搶佔,當搶佔結束,任務線程恢復執行.通過Task_stat()返回的狀態仍然是Task_Mode_RUNNING.
Task線程的鉤子函數:
typedef struct Task_HookSet {
Void (*registerFxn)(Int); /* Register Hook */
Void (*createFxn)(Handle, Error.Block *); /* Create Hook */
Void (*readyFxn)(Handle); /* Ready Hook */
Void (*switchFxn)(Handle, Handle); /* Switch Hook */
Void (*exitFxn)(Handle); /* Exit Hook */
Void (*deleteFxn)(Handle); /* Delete Hook */
};
- registerFxn:在靜態創建任務運行時被初始化調用,在main()之前和中斷使能前被調用.寄存器函數提供了一系列鉤組來存儲相應的鉤子ID,ID可以傳遞給Task_setHookContext() 和Task_getHookContext()來設置或得到相應的鉤子內容.
- createFxn:在創建任務時被調用,這裏包括了靜態創建和使用Task_create() ,Task_construct()動態創建的任務.在Task_disable/enable塊之前並且任務被添加到準備好目錄前,Create鉤子被調用.
- readyFxn:在任務線程準備運行時調用,中斷使能被運行的情況下,Ready鉤子在一個Task_disable/enable塊內被調用.
- switchFxn:在一個任務切換正要發生之前被調用,"prev"和"next"任務處理被傳送給Switch鉤子,對於在SYS/BIOS啓動期間發生的最初的任務切換,"prev"被設置爲NULL
- exitFxn:在一個任務用Task_exit()退出時調用,Exit鉤子被傳送正在退出任務的句柄,它是在Task_disable/enable塊之外並且在任務被從kernel清單移出之前,調用任務的鉤子.
- deleteFxn:當一個任務在運行時被用Task_delete()刪除,調用的一個函數.
例子:
/* ======== TaskHookExample.c ========
* This example demonstrates basic task hook processing
* operation for dynamically created tasks. */
#include <xdc/std.h>
#include <xdc/runtime/Error.h>
#include <xdc/runtime/Memory.h>
#include <xdc/runtime/System.h>
#include <xdc/runtime/Types.h>
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
Task_Handle myTsk0, myTsk1, myTsk2;
Int myHookSetId, myHookSetId2;
/* HookSet functions */
/* ======== myRegister ========
* invoked during Swi module startup before main()
* for each HookSet */
Void myRegister(Int hookSetId)
{
System_printf("myRegister: assigned HookSet Id = %d\n", hookSetId);
myHookSetId = hookSetId;
}
/* ======== myCreate ========
* invoked during Task_create for dynamically
* created Tasks */
Void myCreate(Task_Handle task, Error_Block *eb)
{
String name;
Ptr pEnv;
name = Task_Handle_name(task);
pEnv = Task_getHookContext(task, myHookSetId);
System_printf("myCreate: task name = '%s', pEnv = 0x%x\n", name, pEnv);
Task_setHookContext(task, myHookSetId, (Ptr)0xdead);
}
/* ======== myReady ========
* invoked when Task is made ready to run */
Void myReady(Task_Handle task)
{
String name;
Ptr pEnv;
name = Task_Handle_name(task);
pEnv = Task_getHookContext(task, myHookSetId);
System_printf("myReady: task name = '%s', pEnv = 0x%x\n", name, pEnv);
Task_setHookContext(task, myHookSetId, (Ptr)0xc0de);
}
/* ======== mySwitch ========
* invoked whenever a Task switch occurs/is made ready to run */
Void mySwitch(Task_Handle prev, Task_Handle next)
{
String prevName;
String nextName;
Ptr pPrevEnv;
Ptr pNextEnv;
if (prev == NULL) {
System_printf("mySwitch: ignoring dummy 1st prev Task\n");
}
else {
prevName = Task_Handle_name(prev);
pPrevEnv = Task_getHookContext(prev, myHookSetId);
System_printf("mySwitch: prev name = '%s', pPrevEnv = 0x%x\n",
prevName, pPrevEnv);
Task_setHookContext(prev, myHookSetId, (Ptr)0xcafec0de);
}
nextName = Task_Handle_name(next);
pNextEnv = Task_getHookContext(next, myHookSetId);
System_printf(" next name = '%s', pNextEnv = 0x%x\n",
nextName, pNextEnv);
Task_setHookContext(next, myHookSetId, (Ptr)0xc001c0de);
}
/* ======== myExit ========
* invoked whenever a Task calls Task_exit() or falls through
* the bottom of its task function. */
Void myExit(Task_Handle task)
{
Task_Handle curTask = task;
String name;
Ptr pEnv;
name = Task_Handle_name(curTask);
pEnv = Task_getHookContext(curTask, myHookSetId);
System_printf("myExit: curTask name = '%s', pEnv = 0x%x\n", name, pEnv);
Task_setHookContext(curTask, myHookSetId, (Ptr)0xdeadbeef);
}
/* ======== myDelete ========
* invoked upon Task deletion */
Void myDelete(Task_Handle task)
{
String name;
Ptr pEnv;
name = Task_Handle_name(task);
pEnv = Task_getHookContext(task, myHookSetId);
System_printf("myDelete: task name = '%s', pEnv = 0x%x\n", name, pEnv);
}
/* Define 3 identical tasks */
Void myTsk0Func(UArg arg0, UArg arg1)
{
System_printf("myTsk0 Entering\n");
System_printf("myTsk0 Calling Task_yield\n");
Task_yield();
System_printf("myTsk0 Exiting\n");
}
Void myTsk1Func(UArg arg0, UArg arg1)
{
System_printf("myTsk1 Entering\n");
System_printf("myTsk1 Calling Task_yield\n");
Task_yield();
System_printf("myTsk1 Exiting\n");
}
Void myTsk2Func(UArg arg0, UArg arg1)
{
System_printf("myTsk2 Entering\n");
System_printf("myTsk2 Calling Task_yield\n");
Task_yield();
System_printf("myTsk2 Exiting\n");
}
/* ======== main ======== */
Int main(Int argc, Char* argv[])
{
Task_Params params;
Error_Block eb;
Error_init(&eb);
Task_Params_init(¶ms);
params.instance->name = "myTsk0";
myTsk0 = Task_create(myTsk0Func, ¶ms, &eb);
if (myTsk0 == NULL) {
System_abort("myTsk0 create failed");
}
params.instance->name = "myTsk1";
myTsk1 = Task_create(myTsk1Func, ¶ms, &eb);
if (myTsk1 == NULL) {
System_abort("myTsk1 create failed");
}params.instance->name = "myTsk2";
myTsk2 = Task_create(myTsk2Func, ¶ms, &eb);
if (myTsk2 == NULL) {
System_abort("myTsk2 create failed");
}
BIOS_start();
return (0);
}
/* ======== myIdleFunc ======== */
Void myIdleFunc()
{
System_printf("Entering idleFunc().\n");
Task_delete(&myTsk0);
Task_delete(&myTsk1);
Task_delete(&myTsk2);
System_exit(0);
}
CFG配置文件:
/* Lots of System_printf() output requires a bigger bufSize */
SysMin = xdc.useModule('xdc.runtime.SysMin');
SysMin.bufSize = 4096;
var HeapMem = xdc.useModule('ti.sysbios.heaps.HeapMem');
var heapMemParams = new HeapMem.Params();
heapMemParams.size = 0x80000;
Program.sectMap["HEAPSEC"] = "DDR3";
var Idle = xdc.useModule('ti.sysbios.knl.Idle');
Idle.addFunc('&myIdleFunc');
var Task = xdc.useModule('ti.sysbios.knl.Task');
/* Enable instance names */
Task.common$.namedInstance = true;
/* Define and add one Task Hook Set */
Task.addHookSet({
registerFxn: '&myRegister',
createFxn: '&myCreate',
readyFxn: '&myReady',
switchFxn: '&mySwitch',
exitFxn: '&myExit',
deleteFxn: '&myDelete',
});
Task.defaultStackSize = 4096;
結果:
結果分析:
- 程序啓動,首先執行由registerFxn創建的myRegister函數,該函數在main之前,中斷使能前被調用.
- 執行由createFxn創建的myCreate函數,和由readyFxn創建的myReady函數,這裏還沒有真正意義上調用Task_create來創建任務線程,因此輸出的任務線程名字爲ti.sysbios.knl.Task.IdleTask.
- 之後在主函數中創建的myTsk0,myTsk1,myTsk2線程,依次調用myCreate和myReady函數.
- 調用由switchFxn創建的mySwitch函數,該函數在任務準備運行時調用,由於最開始創建時prev爲NULL,因此執行ignoring dummy 1st prev Task,然後設置下一個的鉤子內容0xc001c0de.
- 正式執行任務線程函數,首先執行myTsk0Func函數,當遇到Task_yield()時,發生任務搶佔,因此執行mySwitch函數,該函數是由switchFxn創建,在一個任務切換正要發生前被調用.
- 接着執行myTsk1Func,同樣,遇到Task_yield()時,發生任務搶佔,跳轉到mySwitch函數中執行,接着繼續執行myTsk2Func,同樣,遇到Task_yield()時,發生任務搶佔,跳轉到mySwitch函數中執行.
- 執行由exitFxn創建的myExit函數,執行完後任務發生切換,又執行mySwitch函數.
- 最後執行myIdleFunc函數.
- 執行由deleteFxn創建的myDelete函數.