Task有5种状态分别是运行running, 就绪Ready,阻塞blocked, terminated终止,inactive停用;前面的状态好理解,停用表示将优先级设为-1,该task不会工作直到其它task将其优先级上升。并发是将处理器从一项任务切换到另一项任务实现的
通过调用某些Task函数以及由信号量或事件模块之类的其他模块提供的函数来暂停当前任务的执行。当前任务还可以终止其自身的执行。 无论哪种情况,处理器都将切换到可以运行的最高优先级任务。
xwr1843存在3个线程:
- Hwi: API is callable from a Hwi thread.
- Swi: API is callable from a Swi thread.
- Task: API is callable from a Task thread.
- Main: API is callable during any of these phases:
- In your module startup after this module is started (e.g. Task_Module_startupDone() returns TRUE).
- During xdc.runtime.Startup.lastFxns.
- During main().
- During BIOS.startupFxns.
- Startup: API is callable during any of these phases:
- During xdc.runtime.Startup.firstFxns.
- In your module startup before this module is started (e.g. Task_Module_startupDone() returns FALSE).
创建任务时,将为其提供自己的运行时堆栈,用于存储局部变量以及进一步嵌套函数调用。 每个堆栈必须足够大以处理正常的子例程调用和一个任务抢占上下文,它是当一个任务由于中断线程为准备就绪的更高优先级的任务而抢占另一个任务时保存的上下文。某些系统配置设置将导致任务堆栈需要足够大以吸收两个中断上下文。
可以使用delete API删除任何不在Task_Mode_RUNNING状态下的动态创建的任务(即不是当前正在运行的任务)。
Task_delete()从所有内部队列中删除任务,并调用Memory_free()来释放任务对象及其堆栈.Memory_free()必须在执行操作之前获取对内存的锁定。如果另一个任务已经对该内存进行了锁定,则执行删除操作的线程将被阻塞直到内存被解锁。
注:
Hook functions: 钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。对每种类型的钩子由系统来维护一个钩子链,最近安装的钩子放在链的开始,而最先安装的钩子放在最后,也就是后加入的先获得控制权。
静态和动态任务的堆栈大小参数均四舍五入为特定于目标的对齐要求的最接近整数倍。对于使用用户提供的堆栈创建的Task,基址和stackSize均要对齐。 基地址增加到最接近的对齐地址。 堆栈大小会相应减小,然后向下舍入为目标特定所需对齐方式的最接近整数倍
为什么要使用hook函数: 做一些定制性工作,比如在任务终止前,调用hook functions可以将有用的信息记录到nvm中。
可以为任务模块指定挂钩函数集,每个集合可以包含以下挂钩函数,它只能被静态配置:
下面举两个函数的具体介绍与使用
typedef enum Task_Mode {
Task_Mode_RUNNING,
// Task is currently executing
Task_Mode_READY,
// Task is scheduled for execution
Task_Mode_BLOCKED,
// Task is suspended from execution
Task_Mode_TERMINATED,
// Task is terminated from execution
Task_Mode_INACTIVE
// Task is on inactive task list
} Task_Mode;
//原型
macro Void Assert_isTrue(Bool expr, Assert_Id id);
//使用
#include
Assert_isTrue(count > 0, NULL);
//disable和restore使用
key = Task_disable();
`critical section`
Task_restore(key);
函数 | 含义 |
---|---|
Void Task_exit(); |
终止当前任务,切换模式Mode_Running->Mode_TERMINATED,只能在Task线程调用 |
Task_Handle Task_getIdleTask(); |
返回空闲task的句柄 |
Task_Handle Task_self(); |
返回当前执行task对象的句柄 |
Void Task_sleep(UInt32 nticks); |
延迟n次ticks, task模式从Mode_RUNNING ->Mode_BLOCKED;真实时间由Clock_tickPeriod确定。经过指定时间后返回到Mode_Ready状态 |
Void Task_yield(); |
如果有同等级任务,使用该函数进行任务切换。 |
Task_Params{ Uint affinity;} |
指定在某个核上运行的任务,如果需要将任务固定到特定内核,则将“亲和力”设置为相应的coreid将强制任务仅在该内核上运行。 |
UInt Task_setPri(Task_Handle handle, Int newpri); |
设置任务A优先级,任务优先级默认最多16,该函数返回旧的优先级。当存在其它任务B时,设置A优先级低于B可实现互斥操作。 |
Void Task_stat(Task_Handle handle, Task_Stat *statbuf); |
检索任务的状态 |
Task.addHookSet(Task.HookSet hook) returns Void |
|
Task_FuncPtr Task_getFunc(Task_Handle handle, UArg *arg0, UArg *arg1); |
通过任务句柄获取任务函数和任务参数 |
信号量管理器提供了一组操作信号量对象的功能, 信号量可用于任务同步和互斥。其实我们学习了QSemaphore,了解这个也是大同小异了。
信号量可以是计数信号量或二值制信号量。 信号量计数跟踪使用post()发布信号量的次数。 例如,有一组在任务之间共享的资源, 这些任务可能会在使用一个之前调用pend()来查看aresource是否可用。
mailbox模块内部使用计数信号量来管理空闲(或全部)mailbox元素。 计数信号量的另一个示例是ISR,它可能会填充多个数据缓冲区以供任务使用,填充每个缓冲区后,ISR将缓冲区放在队列中并调用post(),等待数据的任务将调用pend(),这将仅减少信号量计数,如果计数为0,则返回或阻塞。
pend() | 用于等待信号量,超时参数允许任务等待直到超时/无限期等待/无需等待,返回值用于指示semaphore是否成功发射信号 |
post () | 用于发信号量, 如果任务正在等待这些信号量,则post()将任务从信号量队列中删除,并将其放入就绪队列中。 如果没有任务在等待,则post()仅增加信号量计数并返回, 对于二值信号量,计数始终设置为1。 |
Void Semaphore_registerEvent(Semaphore_Handle handle, Event_Handle event, UInt eventId); |
使用信号量注册一个事件对象,Event_post(event, eventId)和Event_pend(event, eventId, 0, timeout)将会在semaphore_post(), semaphore_pend()调用 |
Semaphore_Handle Semaphore_create(Int count, const Semaphore_Params *params, Error_Block *eb); Void Semaphore_construct(Semaphore_Struct *structP, Int count, const Semaphore_Params *params); |
创建一个semaphore对象 |
Void Semaphore_reset(Semaphore_Handle handle, Int count); |
重置count计数 |
与task,hook一样需要在.cfg文件中定义:
var Semaphore = xdc.useModule('ti.sysbios.knl.Semaphore');
module-wide constants & types
values of type Semaphore.Mode
const Semaphore.Mode_COUNTING;
const Semaphore.Mode_BINARY;
const Semaphore.Mode_COUNTING_PRIORITY;
const Semaphore.Mode_BINARY_PRIORITY;
module-wide config parameters
Semaphore.A_badContext = Assert.Desc {
msg: "A_badContext: bad calling context. Must be called from a Task."
};
Semaphore.A_noEvents = Assert.Desc {
msg: "A_noEvents: The Event.supportsEvents flag is disabled."
};
Semaphore.A_overflow = Assert.Desc {
msg: "A_overflow: Count has exceeded 65535 and rolled over."
};
Semaphore.A_pendTaskDisabled = Assert.Desc {
msg: "A_pendTaskDisabled: Cannot call Semaphore_pend() while the Task or Swi scheduler is disabled."
};
Semaphore.E_objectNotInKernelSpace = Error.Desc {
msg: "E_objectNotInKernelSpace: Semaphore object passed not in Kernel address space."
};
Semaphore.LM_pend = Log.EventDesc {
mask: Diags.USER1 | Diags.USER2,
msg: "LM_pend: sem: 0x%x, count: %d, timeout: %d"
};
Semaphore.LM_post = Log.EventDesc {
mask: Diags.USER1 | Diags.USER2,
msg: "LM_post: sem: 0x%x, count: %d"
};
Semaphore.supportsEvents = Bool false;
Semaphore.supportsPriority = Bool true;
Semaphore.common$ = Types.Common$ undefined;
per-instance config parameters
var params = new Semaphore.Params;
params.event = Event.Handle null;
params.eventId = UInt 1;
params.mode = Semaphore.Mode Semaphore.Mode_COUNTING;
per-instance creation
var inst = Semaphore.create(Int count, params);
mailbox模块提供了一组函数,这些函数可操纵通过Mailbox_Handle类型的句柄访问的mailbox对象。
pend():用于从mailbox中等待一个消息,允许任务等待直到系统时钟指定的超时参数。
当mailbox配置readerEvent事件对象,并且从Event.pend()返回了带有相应的readerEventId的任务时,BIOS_NO_WAIT应该传递给Mailbox_pend()来检索消息。
post()用于向邮箱发送消息。Mailbox_post的timeout参数指定邮箱已满时调用任务等待的时间
Bool Mailbox_pend(Mailbox_Handle handle, Ptr msg, UInt32 timeout); |
如果邮箱不为空,则Mailbox_pend将第一条消息复制到msg中并返回TRUE。 否则,Mailbox_pend会中止当前任务的执行,直到调用Mailbox_post或超时为止 |
Bool Mailbox_post(Mailbox_Handle handle, Ptr msg, UInt32 timeout); |
Mailbox_post在将msg复制到邮箱之前检查是否有空闲的消息槽。 Mailbox_post准备在邮箱上等待的第一个任务(如果有)。 如果邮箱已满并且指定了超时,则该任务将保持挂起状态,直到调用Mailbox_pend或超时为止 |
SYS / BIOS事件是Task与其他线程(例如Hwis,Swis和其他Task)之间,或Task与其它SYS / BIOS对象之间进行通信的一种方式,其中SYS / BIOS对象包括信号量,邮箱,消息队列等。 而任务,Hwis,Swis或SYS / BIOS对象可以发布它们。
Bool Event_Module_startupDone(); |
检测该模块是否完全启动 |
Event_Handle Event_handle(Event_Struct *structP); |
将事件结构体指针转换为事件实例句柄 |
Event_Struct *Event_struct(Event_Handle handle); |
将事件实例句柄转换为结构体指针 |
Event_Handle Event_create(const Event_Params *params, Error_Block *eb); |
分配并初始化一个新的实例返回句柄 |
Void Event_construct(Event_Struct *structP, const Event_Params *params); |
在提供的结构体内初始化一个事件实例 |
UInt Event_pend(Event_Handle handle, UInt andMask, UInt orMask, UInt32 timeout); |
等待事件,MASK表示事件ID |
Queue模块提供了一组函数来处理通过Queue_Handle类型的句柄访问的对象的队列。每个队列包含零个或多个链接元素的序列,这些序列通过Queue_Elem类型的变量引用,它被嵌入为结构中的第一个字段。
在Queue API描述中,在修改Queue之前禁止中断的API称作automic
Queue可以看作是双向列表,Queue_next或者Queue_pre可以连续循环出队列。
总结:Task通过事件与其它BIOS对象如Semaphore,mailbox,message queues 及其它任务对象通信,其中Semaphore用于任务间的同步,message queues用于填充消息,mailbox管理消息队列queues。在实际开发中,TI开发了如mailbox的驱动程序隐藏了EVENT和queues的细节,给出更加安全可靠的核间通信API,仍然使用Semaphore来同步任务,但是理解内核的功能函数对我们理解工程十分有益。