查看文章 |
标题:Main组件分析(二)--TinyOS 1.x(ZT)
2008-08-06 15:00:49
第二组: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(程序)的启动、运行有了个大概了解了吧? |
本文引用通告地址:http://moran.spaces.eepw.com.cn/articles/trackback/item/32700