基于嵌入式操作系统VxWorks的多任务并发程序设计(2)
――任务控制
作者:宋宝华 e-mail:[email]21cnbao@21cn.com[/email] 出处:软件报
4 任务与任务状态
VxWorks
实时内核
Wind
提供了基本的多任务环境。对用户而言,宏观上看起来,多个任务同时在执行。而本质而言,在微观上,系统内核中的任务调度器总是在根据特定的调度策略让它们交替运行。系统调度器需要使用任务控制块(
TCB)
数据结构来管理任务调度功能,
TCB
被用来描述一个任务。
TCB
中存放了任务的上下文(
context)
信息,主要包括程序计数器
PC
、
CPU
内部寄存器、浮点寄存器、堆栈指针
SP
、任务信息等。每一任务都与一个
TCB
关联,当执行中的任务被停止时,任务的上下文信息需要被写入
TCB
;而当任务被重新执行时,必须要恢复这些上下文信息。
VxWorks
的一个任务可能处于如下几种状态:
Ready
:就绪状态(不是运行状态),其他资源已经就绪,仅等待
CPU
,当获得
CPU
后,就进入
Running
状态;
Pended
:阻塞状态,由于等待某些资源(
CPU
除外)而阻塞;
Suspended
:挂起状态,这种状态需要用
taskResume
才能恢复,主要用于调试。不会约束状态的转换,仅仅约束任务的执行;
Delayed
:睡眠状态,任务以
taskDelay
主动要求等待一段时间再执行;
这些状态之间的转换关系如下:
任务状态转换
|
完成方式
|
Ready
->
pended
|
通过
semTake()/msgQReceive()
调用
|
Ready
->
delayed
|
通过
taskDelay()
|
ready
->
suspended
|
通过
taskSuspend()
|
pended
->
ready
|
通过其它任务对
semaGive()/msgQSend()
的调用
|
pended
->
suspended
|
通过其它任务对
taskSuspend()
调用
|
delayed
->
ready
|
延迟期满
|
delayed
->
suspended
|
通过
taskSuspend()
调用
|
suspended
->
ready
|
通过
taskResume()/taskActivate()
调用
|
suspended
->
pended
|
通过其它任务的
taskResume()
调用
|
suspended
->
delayed
|
通过其它任务的
taskResume()
调用
|
5 任务控制
5.1创建任务
VxWorks
程序员创建任务需使用如下
API
:
taskSpawn (char *name, int priority, int options, int stackSize,
FUNCPTR entryPt, int arg1, int arg2, int arg3,
int arg4, int arg5, int arg6, int arg7,
int arg8, int arg9, int arg10);
该
API
的参数定义如下:
name
:任务名;
priority
:任务优先级;
options
:任务选项,下表给出了各种
option
及其含义:
选项
|
16
进制值
|
含义
|
VX_FP_TASK
|
0x0008
|
执行浮点协处理
|
VX_NO_STACK_FILL
|
0x0100
|
不对任务堆栈填充
0xee
|
VX_PRIVATE_ENV
|
0x0080
|
执行一个环境私有的任务
|
VX_UNBREAKABLE
|
0x0002
|
使任务不能断点
|
VX_DSP_TASK
|
0x0200
|
1 = DSP
协处理支持
|
VX_ALTIVEC_TASK
|
0x0400
|
1 = ALTIVEC
协处理支持
|
stacksize
:任务堆栈大小;
main
:任务入口函数;
arg1,…arg10
:任务入口函数参数
下面来看一个具体的例子:
例
1
:创建任务
/* includes */
#include "vxWorks.h"
#include "taskLib.h"
#include "sysLib.h"
int tid;
/* task function */
void myFunc(void)
{
int i;
printf("Hello, I am task %d\n", taskIdSelf()); /* Print task Id */
for (i = 0; i < 10; i++)
{
printf("%d ", i);
taskDelay(sysClkRateGet ( ) / 2);
}
}
/* user entry */
void user_start()
{
printf("ready to begin a new task\n");
tid = taskSpawn("myTask", 90, VX_NO_STACK_FILL, 2000, (FUNCPTR) myFunc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
程序运行,在
VxSim
上输出:
Hello, I am task 14870080
0 1 2 3 4 5 6 7 8 9
taskDelay(sysClkRateGet ( ) / 2)
语句的含义为将任务延迟
0.5S
,因此,
0
、
1
~
9
的数字输出之间间隔
0.5S
。
要特别注意
taskSpawn
函数的
options
参数,在如下几种情况下我们都要将其它
options
与
VX_FP_TASK
做“按位或”操作使得任务支持浮点运算(如果仅包含此选项,则不需进行或操作):
(
1
)执行浮点操作;
(
2
)调用返回任何浮点数的函数;
(
3
)调用参数为浮点数的函数。
例如下列程序启动任务的方式就不正确:
例
2
:创建浮点支持任务
/* task including float calculate */
void floatTask(void)
{
printf("%f", 100 / 30.0);
}
/* user entry */
void user_start()
{
taskSpawn("floatTask", 90, VX_NO_STACK_FILL, 2000, (FUNCPTR) floatTask, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
应该将对
taskSpawn
函数调用的代码改为:
taskSpawn("floatTask", 90, VX_NO_STACK_FILL | VX_FP_TASK, 2000, floatTask, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
5.2 终止任务
例
3
:任务退出
/* includes */
#include "vxWorks.h"
#include "taskLib.h"
#include "sysLib.h"
int tid;
/* task function */
void myFunc(void)
{
int i;
printf("Hello, I am task %d\n", taskIdSelf()); /* Print task Id */
for (i = 0; i < 5; i++)
{
printf("%d ", i);
taskDelay(sysClkRateGet() / 2);
}
exit(0);
for (i = 5; i < 10; i++)
{
printf("%d ", i);
taskDelay(sysClkRateGet() / 2);
}
}
/* user entry */
void user_start()
{
printf("ready to begin a new task\n");
tid = taskSpawn("myTask", 90, 0x100, 2000, (FUNCPTR) myFunc, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0);
}
这次程序仅仅输出:
Hello, I am task 14868640
0 1 2 3 4
这意味着
exit(0)
语句之后的循环
for (i = 5; i < 10; i++)
没有被执行。
extern STATUS taskDelete (int tid);
例
4
:删除任务
/* includes */
#include "vxWorks.h"
#include "taskLib.h"
#include "sysLib.h"
int tid;
/* task function */
void myFunc(void)
{
int i;
printf("Hello, I am task %d\n", taskIdSelf()); /* Print task Id */
for (i = 0; i < 10; i++)
{
printf("%d ", i);
taskDelay(sysClkRateGet() / 2);
}
}
/* another task function:delete my task */
void delMyTaskFunc(void)
{
taskDelay(sysClkRateGet() *4);
printf("ready to delete task\n");
taskDelete(tid);
}
/* user entry */
void user_start()
{
printf("ready to begin new tasks\n");
tid = taskSpawn("myTask", 90, 0x100, 2000, (FUNCPTR) myFunc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
taskSpawn("delMyTask", 90, 0x100, 2000, (FUNCPTR)delMyTaskFunc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
运行输出:
Hello, I am task 14868640
0 1 2 3 4 5 6 7 ready to begin a new task
程序为运行输出
8
、
9
,这是因为在此之前,
myTask
已经被另一个任务――
delMyTask
删除。
任务可能被
taskDelete()
调用删除掉,但这一行为也不一定是安全的。如果我们删除一个获得了某些资源(如二进制信号量等)的任务,则对应的资源将不被释放,到站其它正在等待该资源的任务永远不能获得资源,系统会挡掉。我们可以用
taskSafe()
和
taskUnsafe ()
来保护这种区域,例如:
taskSafe ();
semTake (semId, WAIT_FOREVER);
/* Block until semaphore available */
. .
. .
critical region .
semGive (semId);
semGive (semId);
/* Release semaphore */
taskUnsafe ();
5.3 延迟任务
taskdelay()
提供了一个简单的任务睡眠机制,常用于需要定时
/
延时机制的应用中。它的原型是:
STATUS taskDelay(int ticks /* number of ticks to delay task */);
可以看出使用该函数实现延时的单位为节拍(
tick
)。在
VxWorks
下通常以如下方式调用
taskDelay()
函数:
taskDelay(sysClkRateGet()*n);
其中的
n
是要延迟的时间,以秒为单位。其中的
sysClkRateGet(int ticks /* number of ticks every second */)
函数返回系统的时钟速率,单位是
tick
数
/
每秒。操作系统每秒的
tick
数可以利用
sysClkRateSet()
函数设置。
5.4 挂起/恢复/重启任务
我们可以使用
taskSuspend()
函数挂起一个任务的运行,这个任务只有获得对应的
taskResume()
后才能再次运行,这两个
API
的原型为:
extern STATUS taskSuspend (int tid);
extern STATUS taskResume (int tid);
例
5
:挂起
/
恢复任务
/* includes */
#include "vxWorks.h"
#include "taskLib.h"
#include "sysLib.h"
int tid;
/* task function */
void myFunc(void)
{
int i;
printf("Hello, I am task %d\n", taskIdSelf()); /* Print task Id */
for (i = 0; i < 10; i++)
{
printf("%d ", i);
taskDelay(sysClkRateGet() / 2);
}
}
/* suspend and resume task */
void suspendResumeMyTask(void)
{
taskDelay(sysClkRateGet() *3);
taskSuspend(tid);
printf("my task is suspended\n");
taskDelay(sysClkRateGet() *3);
taskResume(tid);
}
/* user entry */
void user_start()
{
printf("ready to begin new tasks\n");
tid = taskSpawn("myTask", 90, 0x100, 2000, (FUNCPTR) myFunc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
taskSpawn("suspendResumeMyTask", 90, 0x100, 2000, (FUNCPTR) suspendResumeMyTask, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0);
}
运行输出:
Hello, I am task 17753664
0 1 2 3 4 5 my task is suspended
6 7 8 9
这个程序运行
3
秒后,
suspendResumeMyTask
任务挂起了
myTask
,输出“
my task is suspended
”。
suspendResumeMyTask
本身延迟
3
秒后恢复
myTask
,使得
myTask
再次输出“
6 7 8 9
”。显然,“
6 7 8 9
”与“
0 1 2 3 4 5”的输出之间间隔了3
秒以上的时间。
如果我们将上述程序改为:
例
6
:重启任务
/* includes */
#include "vxWorks.h"
#include "taskLib.h"
#include "sysLib.h"
int tid;
/* task function */
void myFunc(void)
{
int i;
printf("Hello, I am task %d\n", taskIdSelf()); /* Print task Id */
for (i = 0; i < 10; i++)
{
printf("%d ", i);
taskDelay(sysClkRateGet() / 2);
}
}
/* reset task */
void resetMyTask(void)
{
taskDelay(sysClkRateGet() *3);
printf("my task will be reseted\n");
taskRestart(tid);
}
/* user entry */
void user_start()
{
printf("ready to begin new tasks\n");
tid = taskSpawn("myTask", 90, 0x100, 2000, (FUNCPTR) myFunc, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0);
taskSpawn("resetMyTask", 90, 0x100, 2000, (FUNCPTR)resetMyTask, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
运行输出:
Hello, I am task 17753664
0 1 2 3 4 5 my task will be reseted
Hello, I am task 17753664
0 1 2 3 4 5 6 7 8 9
我们可以使用
taskRestart()
函数重新启动一个任务,不管任务当前处于什么状态,它都会被重新开始。该
API
的原型是:
extern STATUS taskRestart (int tid);
在例
6
中,程序运行
3
秒后
resetMyTask
启动,它复位了
myTask
,因此
myTask
被重新执行,“
Hello, I am task 17753664
”以及“
0 1 2 3 4 5”被再次输出。
5.5任务钩子
有过
Windows
钩子(
Hook
)编程经验的读者应该对其概念并不陌生,
Hook
作为回调函数,当被挂接后。操作系统发生特定的事情时,将触发这个
Hook
回调函数的执行。
VxWorks
也有钩子的概念,不过比
Windows
要简单许多,主要有
taskCreateHook
、
taskDeleteHook
、
taskSwitchHookAdd
,可以通过如下
6
个
API
来添加和删除这三种
Hook
:
STATUS taskCreateHookAdd (FUNCPTR createHook /* routine to be called when a task is created */ );
STATUS taskCreateHookDelete (FUNCPTR createHook /* routine to be deleted from list */);
STATUS taskSwitchHookAdd (FUNCPTR switchHook /* routine to be called at every task switch */);
STATUS taskSwitchHookDelete (FUNCPTR switchHook /* routine to be deleted from list */);
STATUS taskDeleteHookAdd (FUNCPTR deleteHook /* routine to be called when a task is deleted */);
STATUS taskDeleteHookDelete (FUNCPTR deleteHook /* routine to be deleted from list */);
请看例程:
例
7
:任务钩子
Hook
/* includes */
#include "vxWorks.h"
#include "taskLib.h"
#include "taskHookLib.h" //taskHook
所对应的库
/* task function */
void myFunc(void)
{
int i;
printf("Hello, I am task %d\n", taskIdSelf()); /* Print task Id */
}
/* taskCreatHook */
void myTaskHook(void)
{
printf("task hook function called\n");
}
/* user entry */
void user_start()
{
taskCreateHookAdd( (FUNCPTR) myTaskHook);
taskSpawn("myTask", 90, 0x100, 2000, (FUNCPTR) myFunc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
运行输出:
task hook function called
Hello, I am task 14868640
5.6 其它重要API
与任务控制相关的其它重要
API
还有:
extern STATUS taskOptionsSet (int tid, int mask, int newOptions);
extern STATUS taskOptionsGet (int tid, int *pOptions);
extern char * taskName (int tid);
extern int taskNameToId (char *name);
extern STATUS taskIdVerify (int tid);
extern int taskIdSelf (void);
extern BOOL taskIsReady (int tid);
extern BOOL taskIsSuspended (int tid);
extern WIND_TCB *taskTcb (int tid);
STATUS taskPrioritySet (int tid, /* task ID */ int newPriority /* new priority */ );
extern STATUS taskLock (void);
extern STATUS taskUnlock (void);
本文转自 21cnbao 51CTO博客,原文链接:http://blog.51cto.com/21cnbao/120730,如需转载请自行联系原作者