3、SYS/BIOS--TASK

本模塊介紹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。只有當優先級上上升時纔會被激活。

每個任務對象總是處於以下四個可能的執行狀態之一:

  1. Task_Mode_RUNNING:任務線程正在系統中執行
  2. Task_Mode_READY:任務已經準備好用於編排執行,是否執行取決於處理器
  3. Task_Mode_BLOCKED:任務線程不能被執行,直到特定的時間在系統中發生
  4. Task_Mode_TERMINATED:任務線程被終止了且沒有再執行
  5. 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 */
};
  1. registerFxn:在靜態創建任務運行時被初始化調用,在main()之前和中斷使能前被調用.寄存器函數提供了一系列鉤組來存儲相應的鉤子ID,ID可以傳遞給Task_setHookContext() 和Task_getHookContext()來設置或得到相應的鉤子內容.
  2. createFxn:在創建任務時被調用,這裏包括了靜態創建和使用Task_create() ,Task_construct()動態創建的任務.在Task_disable/enable塊之前並且任務被添加到準備好目錄前,Create鉤子被調用.
  3. readyFxn:在任務線程準備運行時調用,中斷使能被運行的情況下,Ready鉤子在一個Task_disable/enable塊內被調用.
  4. switchFxn:在一個任務切換正要發生之前被調用,"prev"和"next"任務處理被傳送給Switch鉤子,對於在SYS/BIOS啓動期間發生的最初的任務切換,"prev"被設置爲NULL
  5. exitFxn:在一個任務用Task_exit()退出時調用,Exit鉤子被傳送正在退出任務的句柄,它是在Task_disable/enable塊之外並且在任務被從kernel清單移出之前,調用任務的鉤子.
  6. 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(&params);
    params.instance->name = "myTsk0";
    myTsk0 = Task_create(myTsk0Func, &params, &eb);
    if (myTsk0 == NULL) {
        System_abort("myTsk0 create failed");
    }
    params.instance->name = "myTsk1";
    myTsk1 = Task_create(myTsk1Func, &params, &eb);
    if (myTsk1 == NULL) {
        System_abort("myTsk1 create failed");
    }params.instance->name = "myTsk2";
    myTsk2 = Task_create(myTsk2Func, &params, &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;

結果:

結果分析:

  1. 程序啓動,首先執行由registerFxn創建的myRegister函數,該函數在main之前,中斷使能前被調用.
  2. 執行由createFxn創建的myCreate函數,和由readyFxn創建的myReady函數,這裏還沒有真正意義上調用Task_create來創建任務線程,因此輸出的任務線程名字爲ti.sysbios.knl.Task.IdleTask.
  3. 之後在主函數中創建的myTsk0,myTsk1,myTsk2線程,依次調用myCreate和myReady函數.
  4. 調用由switchFxn創建的mySwitch函數,該函數在任務準備運行時調用,由於最開始創建時prev爲NULL,因此執行ignoring dummy 1st prev Task,然後設置下一個的鉤子內容0xc001c0de.
  5. 正式執行任務線程函數,首先執行myTsk0Func函數,當遇到Task_yield()時,發生任務搶佔,因此執行mySwitch函數,該函數是由switchFxn創建,在一個任務切換正要發生前被調用.
  6. 接着執行myTsk1Func,同樣,遇到Task_yield()時,發生任務搶佔,跳轉到mySwitch函數中執行,接着繼續執行myTsk2Func,同樣,遇到Task_yield()時,發生任務搶佔,跳轉到mySwitch函數中執行.
  7. 執行由exitFxn創建的myExit函數,執行完後任務發生切換,又執行mySwitch函數.
  8. 最後執行myIdleFunc函數.
  9. 執行由deleteFxn創建的myDelete函數.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章