Z-stack中OSAL任务机制分析

       首先认识几个名词: 
  协议:标准,约定。 
  协议栈:各层协议的总和,也就是实现这些协议的代码。 
  Z-STACK:zigbee协议栈的名字。 
  OSAL类似于操作系统,是以实现多任务为核心的系统资源管理机制。 
  任务:又可称为线程(个人理解,这里说的可能不对),是一个简单的任务执行过程,在任务执行的过程中CPU完全属于该任务。在Z-STACK中,不同的层拥有不同的任务ID,并且这个任务ID的号码与该层任务的执行次序一致。 
  Z-STACK中重要的三个变量: 
  TasksCnt,TasksEvents,tasksArr[idx]; 
  TasksCnt:任务的总个数,写死了,不会变化。 
  TasksEvents:是个指针,当然同时也是一个数组。其索引idx代表不同的层,由于Z-stack的每一个层都有一个唯一的任务ID,所以idx的值与同层的任务ID值一样。同时,数组的某个元素的值表示了该层有几个事件。下面以应用层为例说明: 
  看tasksEvents的定义与动态内存分配:

uint16 *tasksEvents;
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
  • 1
  • 2

  等价为uint16 tasksEvents[TasksCnt],一个数组,数组中的每个元素都是个两字节的数字,共有TasksCnt个元素。 
  再看OSAL的任务初始化函数

void osalInitTasks( void )
{
  uint8 taskID = 0;

  tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
  osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));

  macTaskInit( taskID++ );
  nwk_init( taskID++ );
  Hal_Init( taskID++ );
#if defined( MT_TASK )
  MT_TaskInit( taskID++ );
#endif
  APS_Init( taskID++ );
#if defined ( ZIGBEE_FRAGMENTATION )
  APSF_Init( taskID++ );
#endif
  ZDApp_Init( taskID++ );
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
  ZDNwkMgr_Init( taskID++ );
#endif
  GenericApp_Init( taskID );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

  发现每个层的初始化函数传入的参数taskID都是递增的,这个taskID也就是本层的任务号,同时也是本层tasksEvents[idx]中idx的值。例如GenericApp_Init( taskID ),这个是应用层的初始化(用户自定义的函数,可能名字会不一样),到这里的taskID值为8的话,那么代表用户自定义任务的tasksEvents[idx]中idx的值也是8。 
  再看tasksEvents[idx]这个元素的值,代表的是某一层的事件,初始值0x0000,按位展开就是:0000 0000 0000 0000,每一个二进制位都可以用来设置一个事件,例如系统事件宏定义:

#define    SYS_EVENT_MSG               0x8000
  • 1

  按位展开就是1000 0000 0000 0000, 
  假如我们定义一个发送事件为

#define   SERIALAPP_SEND_EVT           0x0001,
  • 1

  按位展开就是0000 0000 0000 0001, 
  那么假如tasksEvents[8]= 0x8001,就代表着同时有SYS_EVENT_MSG和SERIALAPP_SEND_EVT两个事件。 
  任务调度中有个死循环,不断检测这个元素的值是否非0,检测到

  do {
    if (tasksEvents[idx])  // Task is highest priority that is ready.
    {
      break;//有事件发生则跳出循环
    }
  } while (++idx < tasksCnt);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

  具体怎么处理每个任务的不同事件,见tasksArr[idx]; 
tasksArr[idx]的定义:

typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event );

const pTaskEventHandlerFn tasksArr[] = {
  macEventLoop,
  nwk_event_loop,
  Hal_ProcessEvent,
#if defined( MT_TASK )
  MT_ProcessEvent,
#endif
  APS_event_loop,
#if defined ( ZIGBEE_FRAGMENTATION )
  APSF_ProcessEvent,
#endif
  ZDApp_event_loop,
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
  ZDNwkMgr_event_loop,
#endif
  GenericApp_ProcessEvent
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

  可以看出tasksArr[idx]是一个指针(也就是数组),指向一个函数(数组元素是函数),函数的参数就是task_id与events。task_id与本层的任务ID是对应的,当然也对应着tasksEvents[idx]中idx的值,而events正好也对应这元素tasksEvents[idx]的值。如

uint16 GenericApp_ProcessEvent( uint8 task_id, uint16 events )
  • 1

  这些函数也就是各个层的处理函数。 
  再来看一下每个事件具体是怎样处理的: 
在处理用户自定义的任务函数——GenericApp_ProcessEvent函数中,有如下语句: 
  如果检测到SYS_EVENT_MSG(也就是tasksEvents[8]的二进制位最高位是1)

  if ( events & SYS_EVENT_MSG )
{
    处理语句
}
return (events ^ SYS_EVENT_MSG);
  • 1
  • 2
  • 3
  • 4
  • 5

  返回值执行了异或操作,也就是说把SYS_EVENT_MSG占据的这一个二进制位清零了,代表这个事件执行完毕,且不会影响其它位所代表的事件。 
例如此时这一层的任务有两个事件SYS_EVENT_MSG与SERIALAPP_SEND_EVT(此事件是自定义的),那么传入的参数events值就是0x8001,执行完return (events ^ SYS_EVENT_MSG)之后值为0x0001,不会影响if ( events & SERIALAPP_SEND_EVT )的判断结果。当然,由于函数已经执行了返回值的操作,无法再执行后边的语句,SERIALAPP_SEND_EVT事件只能等到下一次执行GenericApp_ProcessEvent函数的时候,才有可能处理。 
  再来看一下死循环osal_run_system中的任务处理相关语句:

    events = tasksEvents[idx];//提取需要处理的任务事件
    tasksEvents[idx] = 0;  // Clear the Events for this task.

    activeTaskID = idx;
    events = (tasksArr[idx])( idx, events );//通过指针调用处理函数,处理结束事件按位清零
    activeTaskID = TASK_NO_TASK;

    tasksEvents[idx] |= events;  // Add back unprocessed events to the current task.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

  关注events,先从tasksEvents中提取idx层中的所有事件(可能不是一个),然后作为参数传入(tasksArr[idx])( idx, events )中进行处理,处理函数在处理了一个事件之后会执行一个异或操作,消除已经处理了的一个事件,然后作为tasksArr[idx]的返回值重新赋值给events,再有events传值给tasksEvents[idx],如果这一层的任务中还有别的事件,那么就由主函数的下一次循环来处理。顺带提一下,占据二进制位的顺序越靠左,那么这个事件的优先级就越高。tasksEvents[idx]中idx的值越小,那么这一层任务的优先级也就越高。 

      最后重新复习下自己容易遗忘的点:可以看出tasksArr[idx]是一个指针(也就是数组),指向一个函数(数组元素是函数),函数的参数就是task_id与events。task_id与本层的任务ID是对应的,当然也对应着tasksEvents[idx]中idx的值,而events正好也对应这元素tasksEvents[idx]的值。(红色字体部分为自己容易遗忘的知识点)

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值