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

4 任务与任务状态

 

VxWorks实时内核Wind提供了基本的多任务环境。对用户而言,宏观上看起来,多个任务同时在执行。而本质而言,在微观上,系统内核中的任务调度器总是在根据特定的调度策略让它们交替运行。系统调度器需要使用任务控制块(TCB)数据结构来管理任务调度功能,TCB被用来描述一个任务。TCB中存放了任务的上下文(context)信息,主要包括程序计数器PCCPU内部寄存器、浮点寄存器、堆栈指针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,因此,019的数字输出之间间隔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++)没有被执行。

 

 

 

 

    taskDelete()函数:终止任务并释放任务占用的内存(堆栈和任务控制块空间),其原型为:

extern STATUS taskDelete (int tid);

 

 

 

 

 

    参数tid为任务的ID
    请看下面的例子:

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

 

 

 

 

 

程序为运行输出89,这是因为在此之前,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++)

 

 

 

 

 

  {

 

 

 

 

 

 

你可能感兴趣的:(function,Semaphore,user,任务调度,任务,嵌入式操作系统)