【uTenux实验】任务管理

     任务就是一个无限循环。uTenux提供的任务管理功能是很强大的,包括建立和删除一个任务,启动或退出任务,取消一个任务的启动请求,改变任务的优先级和査询任务状态,使任务进人睡眠状态和唤醒状态,取消唤醒请求,强制释放任务等待状态,使任务状态变成挂起状态、延迟调用任务的执行和禁止任务等待状态。

     任务是一个通过ID来识别的对象,每个任务都有基础优先级和当前优先级,可用来控制任务的执行次序

     任务管理的核心是调度。uTenux会根据创建任务时为任务设置的初始属性和任务运行是获得的动态属性,对任务进行调度。

     创建任务需要为提供T_CTSK类型的任务结构体。这个结构体的定义如下:

typedef struct t_ctsk {
    VP                  exinf;                   /* 额外信息,OS不关注*/
    ATR                 tskatr;                  /* 任务属性*/
    FP                  task;                    /* 任务服务函数入口*/
    PRI                 itskpri;                 /* 初始优先级*/
    W                   stksz;                   /* 用户栈大小 (byte) */
    UB                  dsname[8];               /* Object name */
    VP                  bufptr;                  /* 用户缓存地址*/
} T_CTSK;

任务的属性是一个unsigned long类型的二进制数,它的低位是系统属性,高位是具体实现相关的信息。任务属性有以下几个方面:

tskatr :=(TA_ASM||TA_HLNG)|[TA_USERBUF]|[TA_DSNAME]|(TA_RNG0

TA_ASM                          表示任务是用汇编语言编写的
TA_HLNG                         表示任务是用高级语言编写的
TA_USERBUF                    表示任务是用用户指定的内存区域作为栈空间
TA_DSNAME                     指定DS对象名
TA_RNGn                         指定任务运行在保护级别n

任务结构体的详细介绍,可参考uTenux的内核规范。

将任务结构体传递给任务启动函数tk_cre_tsk就可以创建一个任务。

ID tskid=tk_cre_tsk(T_CTSK* pk_ctsk);

返回值是这个任务的ID,可以作为任务的索引。创建任务只是为任务分配控制块TCB,并不能是任务立即运行或就绪,此时任务处于静止状态。要想是任务变为就绪状态,还需要使用tk_sta_tsk手动启动任务:

ER  ercd= tk_sta_tsk(ID tskid,INT stacd);

其中,tskid为创建任务时候获得的任务ID,stacd是任务的启动代码。stacd用于在启动时传递给任务的参数,这个参数可从启动任务中查询得到,使用这个特性可进行简单的消息传递。

有了以上的概念,就可以开始今天的实验了。

      这个实验参考这uTenux提供的实力sample 01.Task。器质性流程如下所述:

      1、在应用程序入口main函数中进行硬件初始化,之后启动操作系统并创建一个任务initctsk。

      2、在initctsk函数中调用usermain();函数,并通过usermain函数继续调用TaskSample函数。进入实验的主要部分。

     3、在TaskSample中,首先创建三个任务,任务ID分别为:TaskID_A、TaskID_B、TaskID_C。优先级分别为24,26,28.之后启动TaskC。

     4、在TaskC的服务函数中,首先输出一段信息,然后启动TaskA。有余TaskA的优先级较高,立即抢断TaskC开始运行。

     5、TaskA的服务函数中,完成一些信息的输出,之后TaskA进入休眠状态。TasKC重新获得最高优先权开始运行。

     6、TaskC恢复运行后输出一段信息,然后就启动TaskB。同样TaskB也会抢断TaskC的开始运行。

     7、TaskB执行一些动作之后,也会进入休眠状态。TaskC继续执行。进入一个循环。

     8、实验通过串口进行输出。用到的输出函数,参见http://www.cnblogs.com/zyqgold/p/3161753.html

实验的主要代码如下:

//文件TaskSample.c
#include "TaskSample.h"
#include <dev/ts_devdef.h>

void TaskSampleTaskA(W stacd,VP exinf);
void TaskSampleTaskB(W stacd,VP exinf);
void TaskSampleTaskC(W stacd,VP exinf);

static ID TaskID_A;
static ID TaskID_B;
static ID TaskID_C;

ER TaskSample( void)
{
    ER ercd=E_OK;
    T_CTSK ctsk;
  //创建任务A
  ctsk.exinf = NULL;
  ctsk.tskatr = TA_HLNG | TA_RNG0 | TA_DSNAME;
  ctsk.task = (FP)&TaskSampleTaskA;
  ctsk.itskpri = 24;
  ctsk.stksz = 512;
  strcpy(ctsk.dsname,"TaskA..");
  ctsk.bufptr = NULL;
  TaskID_A = tk_cre_tsk(&ctsk);
  if(TaskID_A < E_OK)
  {
    ercd = TaskID_A;
    return ercd;
  }
  //创建任务B
  ctsk.exinf = NULL;
  ctsk.tskatr = TA_HLNG | TA_RNG0 | TA_DSNAME;
  ctsk.task = (FP)&TaskSampleTaskB;
  ctsk.itskpri = 26;
  ctsk.stksz = 512;
  strcpy(ctsk.dsname,"TaskB..");
  ctsk.bufptr = NULL;
  TaskID_B = tk_cre_tsk(&ctsk);
  if(TaskID_B < E_OK)
  {
    ercd = TaskID_B;
    return ercd;
  }
  //创建任务C
  ctsk.exinf = NULL;
  ctsk.tskatr = TA_HLNG | TA_RNG0 | TA_DSNAME;
  ctsk.task = (FP)&TaskSampleTaskC;
  ctsk.itskpri = 28;
  ctsk.stksz = 512;
  strcpy(ctsk.dsname,"TaskC..");
  ctsk.bufptr = NULL;
  TaskID_C = tk_cre_tsk(&ctsk);
  if(TaskID_C < E_OK)
  {
    ercd = TaskID_C;
    return ercd;
  }
  
  tm_putstring((UB*)"Now start task C
");
  tk_sta_tsk(TaskID_C,4);
  
   return E_OK;
} 
 

void TaskSampleTaskA(W stacd,VP exinf)
{
  while(1)
  {
    tm_putstring((UB*)"This is in TaskA
");
    tm_putstring((UB*)"Task_A will sleep
");
    tk_slp_tsk(1000);
  }
}


void TaskSampleTaskB(W stacd,VP exinf)
{
while(1)
{
   tm_putstring((UB*)"Task_B is running
");
   tm_putstring((UB*)"****************
");
   tm_putstring((UB*)"Task_B will sleep
");
   tk_slp_tsk(500);
  }
}


void TaskSampleTaskC(W stacd,VP exinf)
{
  tm_putstring((UB*)"TaskC will start task A
");  
  tk_sta_tsk(TaskID_A,0);
  tm_putstring((UB*)"TaskC will start task B
");  
  tk_sta_tsk(TaskID_B,0);
  tm_putstring((UB*)"Input Cmd,'e' for exit
");
  while(1)
  {
    if('e' == tm_getchar(-1))
    {
      break;
    }
    else
    {
      tm_putstring((UB*)"**************************************
");
      tm_putstring((UB*)"task A will wup
");
      tk_wup_tsk(TaskID_A);
      tm_putstring((UB*)"task B will wup
");
      tk_wup_tsk(TaskID_B);
    }
    tm_putstring((UB*)"Input Cmd,'e' for exit
");
   }
  tm_putstring((UB*)"Task A will stop
");
  tk_ter_tsk(TaskID_A);
  tk_del_tsk(TaskID_A);
  tm_putstring((UB*)"Task B will stop
");
  tk_ter_tsk(TaskID_B);
  tk_del_tsk(TaskID_B);
  tm_putstring((UB*)"Task C will stop
");
  tk_ext_tsk();
}

实验的输出如下:


----------------------------------------------------
        micro Tenux Version 1.6.00(build 0180)     
            Supported MCU is ST STM32F407VG        
  Copyright(c) 2008-2013 by Dalian uLoong Co.,Ltd. 
----------------------------------------------------

Now start task C
TaskC will start task A
This is in TaskA
Task_A will sleep
TaskC will start task B
Task_B is running
****************
Task_B will sleep
Input Cmd,'e' for exit
**************************************
task A will wup
This is in TaskA
Task_A will sleep
task B will wup
Task_B is running
****************
Task_B will sleep
Input Cmd,'e' for exit
Task A will stop
Task B will stop
Task C will stop
Push any key to shutdown the micro Tenux.

<< SYSTEM SHUTDOWN >>

【附1】uTenux的任务状态

1、运行状态:当前任务正在运行

2、就绪状态:任务已经完成运行前的准备,但由于有更高优先权的任务正在运行,使得它不能运行

3、等待状态:由于运行的条件未达到而导致任务不能运行。分三种:

      1、等待状态:等待资源

      2、挂起状态:执行被其他任务强行中断。 等待优先权

      3、等待挂起状态; 任务在同一时间内既在等待状态,也在挂起状态

4、静止状态:任务还未启动或者已经完成执行的状态

5、不存在状态:任务未在系统中注册

【附2】uTenux提供的任务API

tk_sta_sys    启动内核。这个东西是uT自己加进去的,必须在其他系统调用之前执行。需要有一个任务做完参数。启动内核时会立即创建和启动这个任务,即将CPU的控制权交给这个任务。

tk_ext_sys    退出内核

tk_sta_tsk    启动任务,将任务从静止状态转换成就绪状态。需要提供TaskID和另外一个参数stacd。参数Stacd没有什么大用处。

tk_slp_tsk    将任务休眠。参数tmout指的是OS时钟的周期数。如果这个周期结束前调用了tk_wup_tsk,那么任务被唤醒。如果在tmout周期内没有收到tk_wup_tsk唤醒,那么任务自动唤醒。但会返回一个错误代码。

tk_wup_tsk    唤醒由tk_slp_tsk休眠的任务。将任务从等待状态释放

tk_dly_tsk    暂停调用这个函数的任务的执行。但是这时,这个任务的状态还是等待状态而不是休眠状态。。如果要提前终止这个等待时间,可以用tk_rel_wai来执行。

tk_ter_tsk    终止其他任务。强制将tskid指定的任务变为静止状态。任务使用的资源不会自动释放。

tk_del_tsk    删除任务。将任务从静止状态转为不存在状态。所删除的任务必须是静止状态

tk_ext_tsk    退出调用任务。就是调用这个函数的任务自己把自己退出了。自杀,变成静止状态。但是这个函数不会释放任务所使用的资源,需要手动释放。

【附3】关于示例中任务的退出

     uTenux示例中一般都会有两个任务TaskA和TaskB。刚开始做实验的时候曾经问过zhangzl一个问题:当所有的任务都进入等待状态之后,系统是不是就退出了。当时有这个问题,是因为我将两个任务都休眠了之后,OS确实会返回到等待退出状态。今天再次看了看代码,发现示例工程中OS的退出不是因为这个问题!整个示例工程的执行流程是这样的:

1、在main函数中,调用tk_sta_sys((T_CTSK *)&initctsk);启动OS。OS的第一个任务是initctsk

2、在initctsk中调用usermain()来处理其他用户任务,比如TaskA、TaskB。Inittask是一个任务,usermain相当于一个函数。

3、在usermain中调用TaskSample启动和处理其他任务。由于initctsk的优先级最低,当TaskSample中所有任务都不处于等待状态时,initctsk就会继续执行。执行TaskSample之后的代码,只有一行:tk_ext_sys(); 就是退出内核

4、到此,内核就退出了。

5、如果将tk_ext_sys();换成while(1);那么当TaskA、TaskB超时苏醒过来的时候,就会抢断initctsk继续执行。

这个问题归根结底就是刚开始时候,不太明白任务调用方法:第优先级被高优先级抢断,返回时还会回到被抢断的地方继续执行。

原文地址:https://www.cnblogs.com/zyqgold/p/3162001.html