基于嵌入式操作系统VxWorks的多任务并发程序设计(2) ――任务控制

简介:

基于嵌入式操作系统VxWorks的多任务并发程序设计(2

――任务控制
作者:宋宝华  e-mail:[email]21cnbao@21cn.com[/email]   出处:软件报

任务与任务状态

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.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 终止任务

exit( )  :终止当前任务。这个函数是不安全的,任务终止后,其所占据的内存空间并未释放,请看下面的程序:
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,如需转载请自行联系原作者


相关文章
|
消息中间件 存储 缓存
【操作系统入门到成神系列 五】CPU 是如何执行任务的
【操作系统入门到成神系列 五】CPU 是如何执行任务的
【操作系统入门到成神系列 五】CPU 是如何执行任务的
|
物联网 芯片 内存技术
RVB2601开发板上手——GUI与操作系统任务
本文主要测试了LVGL系统GUI函数和操作系统的任务创建
368 0
RVB2601开发板上手——GUI与操作系统任务
|
存储 调度
带你读《C/OS-III内核实现与应用开发实战指南:基于STM32》之三:任务的定义与任务切换
本书分为两部分,第一部分先教你如何从0到1把uC/OS-III内核写出来,从底层的汇编开始讲解任务如何定义、如何切换,还讲解了阻塞延时如何实现、如何支持多优先级、如何实现任务延时列表以及时间片等uC/OS的核心知识点;第二部分讲解uC/OS-III内核组件的应用以及使用uC/OS-III进行多任务编程。
带你读《C/OS-III内核实现与应用开发实战指南:基于STM32》之二:裸机系统与多任务系统
本书分为两部分,第一部分先教你如何从0到1把uC/OS-III内核写出来,从底层的汇编开始讲解任务如何定义、如何切换,还讲解了阻塞延时如何实现、如何支持多优先级、如何实现任务延时列表以及时间片等uC/OS的核心知识点;第二部分讲解uC/OS-III内核组件的应用以及使用uC/OS-III进行多任务编程。