第二组:TOSH_sched_init();for(;;){TOSH_run_task();}
这两个函数的实现在tinyos-1.x\tos\system
目录下的sched.c
源文件中。这个文件就实现了tinyos 1.x
的调度策略,很简单吧?闲话少说,下面分析它的数据结构。
typedef struct {
void (*tp) ();
} TOSH_sched_entry_T;
这个结构体就是tinyos
任务队列里的东东,里面是个函数指针。
enum {
#ifdef TOSH_MAX_TASKS_LOG2
#if TOSH_MAX_TASKS_LOG2 > 8
#error "Maximum of 256 tasks, TOSH_MAX_TASKS_LOG2 must be <= 8"
#endif
TOSH_MAX_TASKS = 1 << TOSH_MAX_TASKS_LOG2,
#else
TOSH_MAX_TASKS = 8,
#endif
TOSH_TASK_BITMASK = (TOSH_MAX_TASKS - 1)
};
上面定义了tinyos任务队列里的最大任务数TOSH_MAX_TASKS,和一个掩码。
//定义tinyos任务队列,这个队列是个循环队列!
volatile TOSH_sched_entry_T TOSH_queue[TOSH_MAX_TASKS];
//“头指针”tinyos任务队列里的第一个不为空的任务的下标
uint8_t TOSH_sched_full;
//“尾指针”如果tinyos任务队列没有满,则是最后一个不为空的任务
//的下一个元素的下标;如果任务队列满则是最后一个任务的下标。
volatile uint8_t TOSH_sched_free;
好了,数据结构分析完了,咱们看看tinyos是怎样实现这个队列的吧,实现一个队列,无非就是初始化,增加队列元素,删除队列元素,判断队列是否为空……,数据结构里最基本的东东,想必大家比我清楚了!(如果这个不清楚,赶紧回去看看数据结构^_^ )。
一初始化
初始化函数很简单,大家肯定都会写了。
void TOSH_sched_init(void)
{
int i;
TOSH_sched_free = 0;
TOSH_sched_full = 0;
for (i = 0; i < TOSH_MAX_TASKS; i++)
TOSH_queue[i].tp = NULL;
}
上面这个就是了,首先是初始化两个“指针”,将它们指向第一个元素。接着是一个for循环将队列里的元素逐个“清零”。
二增加队列元素
bool TOS_post(void (*tp) ()) __attribute__((spontaneous)) {
__nesc_atomic_t fInterruptFlags;
uint8_t tmp;
// dbg(DBG_SCHED, ("TOSH_post: %d 0x%x\n", TOSH_sched_free, (int)tp));
fInterruptFlags = __nesc_atomic_start();
tmp = TOSH_sched_free;
if (TOSH_queue[tmp].tp == NULL) {
TOSH_sched_free = (tmp + 1) & TOSH_TASK_BITMASK;
TOSH_queue[tmp].tp = tp;
__nesc_atomic_end(fInterruptFlags);
return TRUE;
}
else {
__nesc_atomic_end(fInterruptFlags);
return FALSE;
}
}
该函数的参数是个函数指针,大家不要怕它,如果不明白就当它是个整数好了(有点误人子弟的嫌疑,不明白的话大家还是回去复习一下c语言^_*)!
从上面可以看出,该函数核心代码不过三句,
第一句:if (TOSH_queue[tmp].tp == NULL),判断队列里是否满了;如果满了则返回FALSE,如果没满则队列的“尾指针”向后移,一时不明白的网友可以在纸上画画,很快就明白了;移完“尾指针”后,语句TOSH_queue[tmp].tp = tp;将传入的参数加入到队列尾(这个队列尾是移完“尾指针”前的队尾)。
就这样,增加队列元素的动作就完了,很简单吧?比我们学过的数据结构还简单吧?
三、取出队列元素并删除该元素
bool TOSH_run_next_task ()
{
__nesc_atomic_t fInterruptFlags;
uint8_t old_full;
void (*func)(void);
fInterruptFlags = __nesc_atomic_start();
old_full = TOSH_sched_full;
func = TOSH_queue[old_full].tp;
if (func == NULL)
{
__nesc_atomic_end(fInterruptFlags);
return 0;
}
TOSH_queue[old_full].tp = NULL;
TOSH_sched_full = (old_full + 1) & TOSH_TASK_BITMASK;
__nesc_atomic_end(fInterruptFlags);
func();
return 1;
}
这个函数稍为比增加元素那个函数复杂一点点,不过也很简单!
首先是取出队列元素里的值:
void (*func)(void);
old_full = TOSH_sched_full;
func = TOSH_queue[old_full].tp;
就是这三个语句了!声明一个变量,到最后赋值。
接着语句if (func == NULL),判断取出的元素的值是否为空,如果不为空则首先将取出值的原来那个位置清零(TOSH_queue[old_full].tp = NULL;),然后将头指针向后移动(TOSH_sched_full = (old_full + 1) & TOSH_TASK_BITMASK;),最后执行这个函数:
fun();
完了,这个队列简单到极点了吧?但有什么办法了,tinyos 1.x里,这个就是它的任务调度机制:FIFO,并且当队列满时不能够增加新的任务。Tinyos 2.x似乎有所改变。
for(;;) { TOSH_run_task(); }这个语句就让tinyos(程序)一直跑到世界末日了!
void TOSH_run_task() {
while (TOSH_run_next_task())
;
TOSH_sleep();
TOSH_wait();
}
这个函数就不废话了!
从上面还可以看出,整个过程中并没有哪个语句调用bool TOS_post(void (*tp) ())函数,那么谁来增加任务队列中的任务呢?这里举个例子,大家可以转到tinyos-1.x\apps\BlinkTask\目录下,这个例子跟之前的Blink功能一样,不过是使用任务的,在BlinkTaskM.nc文件中Timer的fired事件中有一句:post processing()就将任务添加到任务队列中。换句话说,是应用程序或其它程序将任务添加到任务队列中。
好了!Main组件分析完了,大家对tinyos(程序)的启动、运行有了个大概了解了吧?
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。