除非特别说明,所有的程序都是基于 tinyos-1.3 的,并在 VM 模拟 Win xp 32 位下用 cygwin 调试运行。 思想: Tinyos 使用的是一种特殊的,面向组件的编程思想,这种思想和用惯了面向对象的编程思想的我来说,非常不习惯。 Tinyos 和 nesC 为什么难学? 因为
除非特别说明,所有的程序都是基于 tinyos-1.3 的,并在 VM 模拟 Win xp 32 位下用 cygwin 调试运行。
思想:
Tinyos 使用的是一种特殊的,面向组件的编程思想,这种思想和用惯了面向对象的编程思想的我来说,非常不习惯。 Tinyos 和 nesC 为什么难学? 因为他没有正常的教科书,不像 C , C++ , Java 一样有专门的书或者资料,告诉你需要实现什么功能,要去用到什么类(这里是组件),要去用什么接口,要去用什么函数。 Tinyos 完全没有这些东西,什么都是让你自己去摸索和实践。
所以你有太多的不习惯:
1. 你不习惯 NesC 程序的基本组成是 Component ,每个 Component 是一个 *.nc 文件。这里不像 Java ,大家都知道 java 当中的类是有继承关系,有子类超类等等。你只需要知道一些简单的类,就可以自己扩展学习,即使有没见过的类,也可以找到很详细的介绍和使用方法。 NesC 中没有类的概念,自然也就没有子类超类,没有继承,也就是说,如果你知道有这么一个 Component ,你就可以用这个 Component 可以完成一定的工作。但是如果你不知道这个 Component 呢,尤其是某些底层的 Component 那么嘿嘿 ~~ !! 要么你像我一样,去逼自己看懂系统提供那些的 Component ,要么自己写一个。可问题你现在连最简单的 blink 程序都看不懂,更何况自己写了。所以每个用 NesC 的初学者都和瞎子差不多,说白了,你就是什么都不会的。你的 C 还有 java 经验都根本没用。当然,你会 if 还有 for 语句,这是你在学习 tinyos 中唯一比买菜大妈强的地方。
2. 你不习惯 NesC 程序的中 Component 的使用方法。
NesC 程序中采用了一种所谓的 wiring 的机制来指明 component 与 component 之间的关系。
3. 你不习惯 NesC 程序的中组件的层次结构,也就是接口,方法,属性,函数的关系。
一个 Component 调用其他的 Component 的方式是使用 [interface] ,使用方用关键字 [uses] 的 interface 连接到提供方 [provides] 的 interface 。那么使用方就可以在自己的实现中去使用来自提供方[ interface ]当中的方法。在这里必须强调:
A. 一个组件如果 use 某个 interface ,就必须实现这个 interface 当中的 event 。
B. 一个组件如果 provide 某个 interface ,就必须实现这个 interface 当中所有的 command 。
Interface 我个人把他看成是系统可以识别的方法或者说函数的集合,或者可以说系统的 API ,通常 interface 的内容是几个 command 和 / 或 event 的声明。单看 interface 文件而言, command 和 event 里面的内容是空着的,这点和 java 很像。
command 和 event 是逻辑代码具体实现的地方,他们的具体内容由 provide 他们的 component 来实现。我们所熟悉的内容在这里出现, if 和 for 都可以在这里出现了。我的理解, command 和 event 就是我们熟悉的 c 语言当中的函数,或者 java 当中的方法。因为 command 出现了标志性的括号,比如 init ()。注意,在被调用的时候, Command 用 [call] , event 用 [signal] 。
TinyOS 提供了一些系统 interface ,用来完成基本的程序功能。这里很神奇,因为知道某个 interface 就可以直接完成你的工作,但是作为学习者的我们就非常的累,你必须去熟悉和了解这些 interface 对应的功能和用法。比如 stdcontrol 接口就是用来控制程序流程,有 3 个 command ,分别是 init (), start (), stop ()。再比如 Leds 接口是用来控制传感器上的灯的,有很多 command ,比如 redon (), redoff() 就是控制红灯亮暗。
在 component , interface , command/event 当中是有层次关系的。
一般来说, component 是最高级的,可以理解为 Interface 是 Component 的下层, command/event 是 interface 的下层。 Component. Interface. Command()
4. 你不习惯 NesC 程序的中组件的分类和定义。
常见的 Component 分为两类, [configuration] 和[ module ]
[ configuration ]用来完成 component 之 间的连接。这个是 tinyos 特有的。至少在我学习过的语言中,是独一无二的。我不评价这种机制好或者是不好,但是既然学习了这门语言,也就好好学习这个机制。
这个机制可以说是 tinyos 的核心,所以必须明白,而且要非常清楚! 它涉及到所有的 nesC 程序,所以可以认为这里是学明白 nesC 的关键。
有两个关键字来实现 wiring ,我翻译成[连接]好了。关键字 [ à ]和[ ß ]永远是将一个使用( uses )的接口于一个提供 (provides) 的接口相连接。 也就是说只有使用者组件能够调用提供者组件的接口。反过来就不可以。
Tinyos 中,组件和接口是一个多对多的关系,即一个组件可以连接到很多接口,反过来说,很多组件都可以提供一个相同的接口!(核心!最难理解的地方!)
前面说,不同的组件可以提供相同的接口,如果组件 ComA,ComB 都提供了某一个接口 InterfaceC, 那么,当组件 ComD 需要去访问 InterfaceC 接口时,怎么办? 它访问的到底是 ComA 提供的 InterfaceC 还是 ComB 提供 InterfaceC 的呢? 要知道,虽然接口的名称是一样的,但是不同组件提供的相同接口却是实现不同的功能。
所以配置文件就产生了,它的目的就是声明你要访问的组件名称,并且将它所提供的接口与你真正想要使用的接口相连接。
[module] 才是一个组件的真实实现,用来完成该 Component 的功能,名字常常是 xxxxxM ,可以和 configuration 文件对应起来。 Module 要声明自己使用和提供的借口,所以[ module ]当中是实现 command 和 event 的真正场所,当然还有特殊的内部函数和 task 都在这里实现。在被调用的时候, task 用 post , command 用 [call] , event 用 [signal] 。
5. 你不习惯 NesC 程序的调动机制。
一般来说, TinyOS 中的调度机制比较简单, TinyOS 的调度遵循 FIFS( 先来先服务 ) 规则。没有事件发生时任务列表按着 FIFS 进行处理,当事件发生时发生抢占,产生中断处理。它仅设置一个任务队列。关键字 post 将一个任务添加到任务队列中。 不同的 Task 之间没 有优先级,但 task 可以被 interrupt handler 打断。为防止全局变量等公用数据被非正常修改, nesC 规定只在 task 中进入公共的数据部分。使用 [async] 关键字可以指出某个 command 或者 event 可以在有中断时使用。为了协调任务和中断的执行, nesC 使用 关键字 [atomic] 去指出代码不可被打断。
我不知道 tinyos 和 NesC 的作者是不是吃大便长大的,本来就很晦涩难懂的 nesC 语言也不写个清晰的文档来解释清楚。也许可能是 Tinyos tutorial 的作者英文很差( PS :这个是我美国导师说的)。在这里,请本 blog 的看官请注意,如果你是美式教材或者 tinyos 官方文档的支持者,你根本就不应该来看这篇文章,因为这篇文章就写给看不懂官方文档的同学看的。你看得懂官方文档或者说喜欢美国人的思维方式来介绍新知识新技术的方法,说明你英语和计算机科学水平比我高,所以也不用看加上我自身理解的 NesC 程序解释和指导的博文。虽然我在美国,但是我一点也不觉得美国的教材写的好。相反,根据我多年的教学经验和我自身的学习经验,我个人是非常认同和支持国内的很多 c 或者 java 教材的写法,层次感很清楚,介绍的内容由浅入深,知识慢慢学来。