nesC 命令调用与事件触发原
之所以想写这个博文呢,是因为最近准备物联网挑战赛,一直在学这个tinyOS和无线通信这方面,看了很多文档,零零散散地学了一些东西,怕自己学了就忘,也是要自己印象深一点,就干脆写下来。
首先,tinyOS这个系统是用nesC写的,nesC虽然是C的变种语言,但是就我自己感觉,除了一些语法比较相似之外,其他的完全不一样......(浩博士之前还说比赛就是C语言,上大当啊。)所以我们简单先说一下nesC。
nesC 是面向组件化编程的语言,它通过接口(interface)连接(wiring)已有的组件,从而完成一个应用的开发。命令(command)的调用(call)和事件(event)的触发(signal)是nesC中非常重要的部分。
interface X { command int f(); event void g(int x); } 例1 接口X module M { provides interface X as P; uses interface X as U; provides command void h(); }implementation { ... } 例2 模块M configuration C { provides interface X; provides command void h2(); } implementation { components M; X = M.P; M.U -> M.P; h2 = M.h; } 例 3 配件 C
基本术语介绍:
1.命令(command)和事件(event):组件或接口中前面关键字声明为 command 和 event 的函数,如例 1 中的 int f()就是一个命令函数,而 void g(int x)就是一个事件函数;
2.接口(interface):是一系列函数的声明,包含一系列命令和事件,例1中X就是一个接口,在这个接口中声明了命令int f()函数和事件void g(int x)函数;
3.组件(component):是 nesC 的基本单元,包括模块(module)和配件(configuration),如例 2 中的模块 M 和例 3 中的配件 C;
4.模块(module):具体实现组件的逻辑功能,如例2中的M就表示一个模块;
5.配件(configuration):具体描述不同组件之间的连接(wiring)关系,如例3中的C就表示一个配件;
6.接口实例:在组件中定义的特定接口类型的实例,如例 2 中模块 M 提供接口实例 P,接口实例 P 就是接口类型 X 的一个实例;
7.规范元素(specification element):包括命令,事件和接口实例等;
8.内部规范元素(internal specification element):在组件的实现(implementation)中的组件列表中所包含组件的规范元素,如在例 3 中配件 C 的实现中的组件列表包含组件 M,那组件M 所提供的接口实例 P,使用的接口实例 U 以及所提供的命令 void h()函数就是配件 C 中的内部规范元素;
9.外部规范元素(external specification element):在组件的定义(specification)中所提供和使用的规范元素,如在例 3 中配件 C 的定义中提供了接口实例 X 和命令 void h2()函数,则接口实例 X 和命令 void h2()就是配件 C 的外部规范元素;
10.端点(endpoint):在连接两端的规范元素,如例 3 配件 C 中的 M.U -> M.P,那 M.U 和M.P 就是端点。
在了解基本术语的基础上,我们来说说在nesC中组件之间是如何通过连接而实现互相的调用的。组件(无论是模块还是配件)使用和提供了接口、命令和事件等规范元素,规范元素之间的互相调用是通过配件连接起来的。
在连接关系两端的规范元素必须满足以下2种情况:
a、端点为事件或命令,则该端点必须是外部规范元素,如在例3中配件C的连接关系 h2 = M.h中,端点h2是一个命令,且h2是一个外部规范元素(因为它是在配件C的定义中提的);
b、端点为组件的接口实例、事件或是命令,该组件必须是组件列表中的元素,如在例3中配件C的连接关系h2 = M.h中,端点M.h是组件M中的命令void h(),且组件M在配件C的组件列表中。
在配件文件中有三种连接关系,分别是“=b”、“->”、“<-”,下面分别说明一下连接关系的规则:
“=”连接关系两端元素必须要满足下列两种关系中的一种:
a、一端是内部规范元素,另一端是外部规范元素,那么两者要么都是被提供,要么都是被使用,如在例3中配件C的连接关系中X = M.P,由前面的介绍我们知道接口实例X是外部规范元素,接口实例M.P是内部规范元素,且X和M.P都是被提供的;
b、两端都是外部规范元素,那么一端被提供,一端被使用,这种情况在我们例子中没有出现。
“->”连接关系两端必须都是内部规范元素,左端被使用,右端被提供**,如在例3中配件C的连接关系中M.U -> M.P,由上面的介绍我们也可以知道M.U和M.P都是内部规范元素,M.U在“->”的左端,所以M.P必须被使用,M.P在“->”的右端,所以M.P必须被提供,从模块M的定义中我们知道M.U和M.P是满足这两种关系的;
“<-”连接关系跟“->”刚好相反,右端是使用者,左端是提供者;
由之前介绍可知在连接的两端都是规范元素,当然规范元素包括接口实例、命令和事件,而且接口实例中包含的也是命令和事件。那么通过配置进行连接后的规范元素之间到底是如何实现调用?到底谁调用谁呢?或者换句话说,call和signal到底是如何实现对命令和事件的调用的?在这里还得注明一点,在nesC中call是用来调用声明为command的函数的,而signal是用来触发(也是一种调用,换种名字而已)声明event的函数的。既然所有的规范元素最根究底都牵涉到了命令和事件,而命令和事件其实就是一个个函数,所以不妨先规定在连接(wiring)的两端的规范元素都是函数,因为比如连接的两端都是接口实例,那这个接口实例进行调用的话也是这两个接口实例包含的命令或事件之间进行调用,所以完全可以假设所有在连接两端的规范元素都是函数。这样前面的例子3配件C中的函数就有C.X.f, C.X.g, C.h2, M.P.f, M.P.g, M.U.f, M.U.g, M.h 8个。那之前所提到的问题就成了在配件中连接两端的函数到底是哪端调用哪端的问题了。下面分析一下进行连接后的函数之间调用关系,以下有4个条件,如果满足其中之一,则这个函数就是被调用者(callee),否则这个函数就是调用者(caller):
1、 如果该函数是一个内部规范元素,且这个函数是一个被提供的命令或事件,则该函数被调用,在例3中的连接关系h2 = M.h中,M.h是个内部规范元素,且M.h是一个被提供的命令,所以M.h就是个被调用者;
2、 如果该函数是一个外部规范元素,且这个函数是一个被使用的命令或事件,则该函数被调用,该情况在例子中没有出现;
3、 如果该函数是接口实例的命令,且这个接口是内部规范元素并被提供,或该接口是外部规范元素并被使用,则该函数被调用,在例3中的连接关系X = M.P; M.U -> M.P中,M.P.f是个接口实例P的命令,且接口实例P是内部规范元素且被提供,所以M.P.f就是一个被调用者;
4、 如果该函数是接口实例的事件,且这个接口是外部规范元素并被提供,或该接口是内部规范元素并被使用,则该函数被调用,在例3中的连接关系M.U -> M.P中,M.U.g是个接口实例U的命令,且接口实例U是内部规范元素且被使用,M.U.g是接口实例M.U的事件,所以它就是一个被调用者,同样在连接关系X = M.P中,X是个外部规范元素且被提供,C.X.g是接口实例C.X的事件,所以它就是一个被调用者。
如果函数满足以上4个条件之一,则该函数是被调用函数,否则该函数调用别的函数,所以剩下的C.X.f, C.h2, M.P.g, M.U.f就都是调用者。那么在例3中的配件C中连接的函数之间的互相调用关系如下:C.X.f调用M.P.f ,C.h2调用M.h,M.P.g调用M.U.g和C.X.g,M.U.f调用M.P.f。像M.P.g调用M.U.g和C.X.g两个函数,这种调用关系就叫扇出(fan-out); 而C.X.f和M.U.f都调用了M.P.f,这种调用关系就叫扇入(fan-in)。
以上关于事件与命令的分析比较深入,或许会让人看得一知半解迷迷糊糊的,但其实很多的时候其实我们并不需要那么清楚的知道究竟是谁响应了谁的调用,有时候仅仅需要知道是什么组件完成什么功能就可以了,至于其他的导通工作,我们只需要copy代码块就行了。
浙公网安备 33010602011771号