SEM旗语模块
使用一组函数通过旗语对象的句柄来管理旗语的使用。DSP/BIOS内核提供的旗语实际上是信号量旗语,该旗语管理模块通过对旗语的计数来完成任务线程的同步和相互作用。
SEM_pend常用函数来等待旗语。这个函数可以带一个超时参数,以控制允许任务等待的时间,如指定时间、无限等待或不等待。SEM_pend函数的返回值用来指示是否成功收到旗语。
SEM_post函数用于发送旗语。如果一个任务正在等待该旗语,SEM_post函数将该任务的状态改为就绪;若没有任务正在等待旗语,SEM_post函数仅仅简单地对旗语的计数器加1,然后返回。
两个重要的函数
(1) SEM_pend
语法 status=SEM_pend(sem,timeout);
参数 SEM_Handle sem;
Uns timeout;
返回值 Bool status;
功能 如果旗语计数器大于0,SEM_pend函数会对计数器进行
减1操作,并返回TURE。否则,SEM_pend函数会暂停当前任务的运行(挂起),直到该函数指定的旗语到达。如果超时参数不等于SYS_FOREVER或0,当前任务等待时间(如共有timeout个系统报警时钟)。在这段之间之后当前任务自动变为就绪状态。如果超时参数为SYS_FOREVER,那么直到有线程调用SEM_post 函数,当前任务会一直处于暂停(挂起)状态。如果超时参数为0,SEM_pend会立即返回。当超时参数为0或者已经到达指定的等待时间,还没有旗语到达,函数SEM_pend会返回FALSE,否则,返回TURE。
注意: 当旗语计数器为0,而超时参数不为0时(表示需要当前线程等待旗语),调用函数会发生任务切换。
(1) SEM_post
语法 SEM_post(sem);
参数 SEM_Handle sem;
返回值 Void
功能 SEM_post 函数将使处于等待旗语的任务变为就绪(Ready)状态,而该任务在等待旗语时处于暂停(Blocked)状态。 如果没有等待旗语的任务,函数SEM_post仅仅对信号旗语计数器加1并返回。在调用该函数时,若更高级任务处于就绪状态,则会发生任务切换。
先比较一下两个例子:
TI提供的C6711的例程,全部已经改成642,程序没变化,BOIS配置也一样。
#include
#include
#include
#include
#include
#include
#include
#include
#include "semcfg.h"
#define NUMMSGS 3
#define NUMWRITERS 3
typedef struct MsgObj {
QUE_Elem elem;
Int id;
Char val;
} MsgObj, *Msg;
Void reader();
Void writer(Arg id_arg);
QUE_Obj msgQueue;
QUE_Obj freeQueue;
Void main()
{
LOG_printf(&trace, "semtest example started.
");
}
Void initTask()
{
Int i;
MsgObj *msg;
Uns mask;
QUE_new(&msgQueue);
QUE_new(&freeQueue);
mask = TRC_LOGTSK | TRC_LOGSWI | TRC_STSSWI | TRC_LOGCLK;
TRC_enable(TRC_GBLHOST | TRC_GBLTARG | mask);
msg = (MsgObj *)MEM_alloc(0, NUMMSGS * sizeof(MsgObj), 0);
if (msg == MEM_ILLEGAL) {
SYS_abort("Memory allocation failed!
");
}
for (i = 0; i < NUMMSGS; msg++, i++) {
QUE_put(&freeQueue, msg);
}
}
Void reader()
{
Msg msg;
Int i;
for (i = 0; i < NUMMSGS * NUMWRITERS; i++) {
SEM_pend(&sem, SYS_FOREVER);
msg = QUE_get(&msgQueue);
LOG_printf(&trace, "read '%c' from (%d).", msg->val, msg->id);
QUE_put(&freeQueue, msg);
}
LOG_printf(&trace, "reader done.");
}
Void writer(Arg id_arg)
{
Msg msg;
Int i;
Int id = ArgToInt (id_arg);
for (i = 0; i < NUMMSGS; i++) {
if (QUE_empty(&freeQueue)) {
SYS_abort("Empty free queue!
");
}
msg = QUE_get(&freeQueue);
msg->id = id;
msg->val = (i & 0xf) + 'a';
LOG_printf(&trace, "(%d) writing '%c' ...", id, msg->val);
QUE_put(&msgQueue, msg);
SEM_post(&sem);
}
LOG_printf(&trace, "writer (%d) done.", id);
}
本程序静态建立了三个优先级为1的写任务线程,写函数writer(Arg id_arg)带有参数,这个参数由静态配置传递,优先级为2读任务线程以及最高优先级15的初始化任务线程,初始化保证读线程总不会独到空的队列。
读函数线程比写函数线程优先级高,因此读函数一直处在等待旗语的状态,一旦写函数在队列里面写入一个字符,并且发送旗语信息给读函数,读函数马上从队列里面把数据读出。
改变线程间的优先级,会得到不一样的运行结果!
假若TSK_yield();不被注释掉,另外设置任务线程reader0优先级5、 writer0和writer1优先级为1、 writer2优先级为2、那么读任务还总是处在最高的优先级,优先运行;writer2具有中间优先级,优先运行;writer0和writer1具有相同的优先级并行运行,本来先开始的先运行,但是由于writer0执行到TSK_yield();会自动放弃当前线程,重新排序,这个时候排序表里面的writer1开始运行。
运行结果如下:
0 semtest example started.
1 (2) writing 'a' ...
2 read 'a' from (2).
3 (2) writing 'b' ...
4 read 'b' from (2).
5 (2) writing 'c' ...
6 read 'c' from (2).
7 writer (2) done.
8 (0) writing 'a' ...
9 read 'a' from (0).
10 (1) writing 'a' ...
11 read 'a' from (1).
12 (0) writing 'b' ...
13 read 'b' from (0).
14 (1) writing 'b' ...
15 read 'b' from (1).
16 (0) writing 'c' ...
17 read 'c' from (0).
18 (1) writing 'c' ...
19 read 'c' from (1).
20 reader done.
21 writer (0) done.
22 writer (1) done.