标签:
说明:本系列文章翻译自Contiki之父Adam Dunkels经典论文,版权归原作者所有。
Contiki是由Adam Dunkels及其团队开发的系统,研读其论文是对深入理解Contiki系统的最佳资料。
-----------------------------------------------------------------------------------------------------------------------------------------------------------无线传感器网络由大量微小的联网可通信设备组成。对于大规模网络而言,动态地下载代码到网络中是极其重要的。在本论文中,我们描述了一个支持动态地加载、替换个人储蓄和服务的轻量级操作系统——Contiki。Contiki基于事件驱动(event-driven)内核,并提供可选的抢占式多线程。在一个资源受限的环境中,Contiki能做到保持基础系统轻量、紧凑的同时,还可以灵活地动态加载和卸载程序和服务。
无线传感器网络由大量具有无线通信能力的微传感器设备组成。这些传感器设备可以自治地构成网络并传输数据。通常情况下,这些传感器设备是资源受限的。板载电池或者太阳能面板只能提供有限的电源供应。此外,微小的尺寸和低廉的价格也限制着系统的复杂性。典型的传感器的配置包括8位微控制器、大约100kb的代码存储空间和小雨20kb的RAM。根据摩尔定律,这些设备在将来会越来越小、越来越廉价。尽管这意味着传感器网络可以被扩展到更大的范围,但却不一定说明相关资源会受到更小的限制。
作为一个为传感器节点设计操作系统的设计者连说,其面临的挑战在于找到一个轻量级的机制和抽象,以在资源受限的设备上提供足够丰富的执行环境。我们已经开发了Contiki——针对资源受限环境而开发的操作系统。Contiki提供了个人程序和服务动态加载、卸载的功能,其内核基于时间驱动(event-driven),并支持抢占式多线程机制。哪些明确需要多线程的程序将被链接为一个库,从而实现可抢占式多线程。本篇论文的贡献主要在两个方面。第一是灵活性。一个资源受限传感器设备之上可加载程序和服务。第二个通用性。抢占式多线程不必再内核最底层实现,而是构建于事件驱动内核顶层作为应用程序库。这允许基于线程的程序运行在基于事件的内核之上,减少系统各部分重入带来的开支、栈空间。
随着不同的传感器设备平台的增加,我们需要一个可以在不用硬件平台移植的通用软件基础架构。当前有效的传感器平台上都载有完全不同的配置。由于应用程序相关的特性,我们期望着部分在将来不会改变。在今天,平台间的统一不变的特性只有一个——不使用段机制或者内存保护机制的CPU架构。在这种架构下,程序代码被存放于可重新编程的ROM,程序数据被存放于RAM。我们设计了Contiki操作系统,其提供CPU多线程抽象(且是唯一的抽象),并支持程序和服务的可加载性。考虑到传感器网络中应用的相关特性,我们认为其他抽象被设计为库或者服务、提供动态机制更合适
本论文剩下的部分以如下的形式组织:第2章回顾相关的工作,第3章描述Contiki系统的框架,第4章描述Contiki内核的设计,第5章描述Contiki服务的概念,第6、7章描述Contiki如何处理库、如何处理通信支持,第8章描述可抢占式多线程的实现,第9章描述我们使用该系统的经验,最后,第10章作总结。
与Exokernel[11]和Nemesis[16]类似,Contiki尽量减少抽象的数量,以保持内核尺寸的最小化。抽象以库的形式实现,并且几乎有用下层硬件的完全访问权限。Exokernel追求性能、Nemesis追求服务质量,Contiki追求减小尺寸、维持灵活。与Exokernel不同的是,Contiki不支持保护机制,因为Contiki的硬件在设计时就不支持内存保护。
程序通过程序加载器加载到系统中。程序加载器通过使用通信栈或者直接使用像EEPROM之类的附加存储设备来获得程序二级制文件。典型地,在程序被烧写内存之前是存放在EEPROM里,然后被加载系统。
Contiki内核为所有进程执行提供唯一的共享的栈。由于事件处理器的调用时栈是倒回的(rewound),异步事件的使用减少了栈空间。
为了能够支持一个根本的实时执行体,Contiki从不关闭中断。由于这个原因,Contiki不允许中断处理器派遣事件,因为那会导致在事件处理器中产生竞争条件。相反地,内核提供了一个轮询标记,用以请求轮询事件。这个标记给中断处理器提供了一个请求立即轮询的方法。
将程序被加载到内存后,加载器会调用程序的初始化函数。程序的初始化函数可能会开始或者替换一个或多个进程。
在传感器网络中,总是希望当网络不活动时能够关闭节点电源以达到减小能量消耗的目的。电源保护机制既依赖于应用[18],也依赖于网络协议[20]。Contiki内核包含了非显式节电抽象,但是需要应用去指定去实现这个机制。为了能够帮助应用决定什么时候关闭系统电源,时间调度器公开了时间队列的大小。当没有事件被调度的时候,可以根据这个信息来关闭处理器。当处理器响应中断被唤醒的时候,通过轮询处理器处理外部事件。
图2.应用函数调用服务的过程
在Contiki中,服务是为其他进程提供特定功能的进程,可以看成是共享库的一种形式。服务在运行时可以被动态地替换,因此也必须被动态地链接。典型的服务包括通信协议栈、传感器设备驱动程序、高级别功能(比如传感器数据处理算法)。
服务层紧紧靠近内核,负责管理服务。具体地说,服务层负责跟踪正在运行的服务,并提供一种方法以找到被安装的服务。一个服务由一个文本字符串所标识,用于描述该服务。服务层使用通用的字符串匹配方法来查询已经安装的服务。
服务由服务接口和实现该接口的进程组成。服务接口由一个版本号和一个函数表组成。函数表由指向该函数接口的函数指针组成。
应用程序通过根库(stub library)与服务通信。根库被链接到应用程序,然后使用服务层找到服务多对应的进程。一旦一个服务被定位,根将服务进程的进程ID缓存起来,用于将来所有的服务请求。
程序通过服务接口根调用服务。在调用服务的过程中,程序不必知道他所调用的接口是用于服务的特殊接口。服务第一次被调用时,服务接口根会在服务层查找服务。如果指定的服务存在,查找时将返回一个指向该服务接口的指针,而后存在服务接口中的版本号将与根中的版本号进行检查。除了版本号之外,服务接口中还包含了指向服务函数的指针。如果服务根中的版本号与服务接口中的版本号成功匹配,服务接口根将调用被请求的函数。
与所有的进程一样,服务也可以在正在运行的Contiki系统中被动态地加载和替换。由于服务进程的进程ID用于标识进程,如果服务进程被替换,保留其进程ID是至关重要的。基于这个原因,内核提供了特殊的机制用于替换进程和保留进程ID。
当一个服务被替换的时候,内核通过给服务进程邮寄一个特殊的事件。作为对该事件的响应,服务必须从系统中移除自己。
许多服务内部有一个状态,用于传递到新进程。内核提供了一种方法,将一个指针传递到新的服务进程,新服务进程可以根据传入的状态产生一个状态描述符,用于描述进程的状态。保存有状态的内存必须从共享源里分配,因为当老进程被移除的时候会重新分配进程内存。
服务的状态描述符中包含服务的版本号,因此内核不会尝试去加载具有不兼容版本号的相同服务。
Contiki内核只提供最基本的CPU复用和事件处理特性,系统其它部分以系统库的形式实现,并被可选择地链接到程序中。程序与库的链接有三种方式。第一种,作为核心的一部分的库,被静态地链接到程序中。第二种,作为可加载程序的一部分的库,被静态地链接到程序中。第三种,程序可以调用服务来实现一个特定的库。以服务形式存在的库,可以在运行时被动态地替换。
典型地,运行时的库,比如经常被使用的库,最好放到Contiki核心。很少被使用,或者应用相关的库,更适合与可加载程序链接。作为核心一部分的库,总是存在系统中,因此不必被包含到可加载程序库中。
举个例子,假设程序使用函数memcpy()拷贝内存,使用atoi()将字符串转变为整数。函数memcpy()是经常被使用的C库函数,而atoi()被用得不是那么多。因此,在这个特殊的例子中,memcpy()将被包含到系统核心,而atoi()不会被包含。当程序被链接成二进制时,函数memcpy()的静态地址将会被再次链接到核心。然而,C库中实现auoi()的那部分目标代码必须被包含到程序二进制中。
图3.通信栈
通信是传感器网络中的基础概念。在Contiki中,为了使运行时替换成为可能,通信是以服务的形式实现的。多个通信栈被同时加载。通过实验研究发现,这可以被用于评估和比较不同的通信协议。而且,正如图3所示,一个通信栈可能会被分成不同的服务,这就是的运行时替换部分通信栈成为可能。
通信服务使用服务机制去调用其他服务,并使用同步事件去与应用程序通信。由于同步事件处理器被要求一旦运行起来就必须运行到结束,这就使得所欲的通信处理使用一个buffer成为可能。设备驱动程序读取接收到的包到通信buffer中,然后就调用上层的通信服务。通信栈负责处理包头,并邮寄一个同步事件给应用程序,告知已经接收到此包。应用程序对包的内容作出响应,然后可选地在buffer中保持一个响应,最后将控制权返回通信栈。通信栈预先准备需要发送到外部的新的包头,然后将控制权限交给设备驱动程序,这样就完成了一个包的传输。
在Contiki中,可抢占式多线程是在基于事件的内核之上以库的形式实现的。库是以可选的形式被链接到应用程序中的,即应用程序明确指出需要进行多线程模型操作的时候,才将库链接到应用程序中。库被分为两类:与平台独立的部分——用于与事件内核相互作用,以及与平台相关的部分——用于实现栈的切换和抢占机制。通常,抢占通过一个定时器中断实现,保持处理器的寄存器到栈中,然后切换回内核栈。在实际中,当移植平台相关部分库时,只需要重写很少的代码。举个例子,对于MSP430平台,只用了25行C代码。
与常规Contiki进程不同,每个线程都需要一个独立的栈。库中提供了必要的栈管理函数。在线程明确地推出、或者被抢占之前,每个线程在各自的栈上执行。
图4.多线程库的API
图4展示了多线程库中的API。它由六个函数(mt_yield(), mt_post(), mt_wait(), mt_exit(),mt_start() and mt_exec())组成。mt_exec()函数被事件处理器调用,用于执行实际的线程调度。
我们已经用Contiki操作系统实现了很多传感器网络应用程序,比如多跳路由、运动检测。
我们已经实现了一个简单的空中编程协议。该协议使用点对点通信,传输一个单一的二进制文件到选定的集线器。该二进制文件存储在EEPROM,并在本节点接收到完整的程序时,被广播到邻居节点。如果发生包丢失,可以由邻居节点发出低电平信号作为示意,然后由集线器节点修复。我们打算在将来实现更好的协议,比如Trickle算法。
我们做了一个实验,动态地分布40个节点到报警系统中。我们使用了空中重编程方法和人工有线重编程方法。最初,程序加载机制不是非常完善,在我们的实验中不能使用程序加载。我们的目标程序大约为6KB,加上Contiki核心和C库,完整的系统镜像接近30KB。使用人工有线重编程的方法,为一个独立的传感器节点编程花费了超过30秒。而一共40个点检,对整个网络冲编程至少需要30分钟。相反地,通过空中重编程的方法,只使用了2分钟,这减小了一个数量级。
表1.编译后的代码尺寸(字节)
对于一个为受限设备开发的操作系统,代码的尺寸和RAM的利用率必须足够小,这样才能给运行在系统之上的应用留下足够的空间。表1展示了Contiki在两个架构上的代码尺寸和RAM使用率:TI MSP430和Atmel AVR。表中的数字翻译了核心组件和应用案例——传感器数据复制服务 的尺寸。这个数据复制服务由服务接口根和和服务自身实现两部分组成。目前,程序加载器只在MSP430平台上实现了。
Contiki的代码尺寸比TinyOS大,比Mantis系统小。由于提供了不同的服务,contiki的事件内核比TinyOS大是理所当然的。TinyOS的事件内核仅提供了一个FIFO事件队列调度器,而Contiki内核既支持FIFO事件,也支持优先级的轮训处理器。而且,对于TinyOS这类系统而言,Contiki的灵活性也需要更多的运行时代码。
RAM的需求依赖于系统配置的最大进程个数(p),异步事件队列的最大尺寸(e)以及多线程操作中的线程栈大小(s)。
图5.在可抢占计算期间响应时间有很少的增量
抢占式的目的是使长时间运行更有意义,能够响应到来的事件,比如传感器输入或者到来的通信数据包。图5描述了当Contiki正在运行一个需要8秒才能完成的可抢占式线程是如何响应到来的通信数据包的。曲线由200个往返ping包(每个包40直接)测试而来的。ping包的过程大约从第5秒开始到第13秒结束。在这期间,往返的时间稍微增加了但是依然能够响应ping包。
我们的测试方法是每隔200ms,从1.4GHz的PC通过57600kb/s的速度向运行Contiki的ESB节点发送包。数据包之所以通过串行线而不是无线连接,是为了避免无线电影响,比如位错位或者MAC冲突。
我们已经将Contiki移植到了很多架构上,包括TI MSP430和Atmel AVR。其他人已经将系统一直到Hitachi SH3 和Zilog Z80。移植过程包括写启动代码、设备驱动、架构相关的程序加载器以及多线程库中的栈切换代码。内核和服务层不需要做任何改动。
由于内核和服务层不需要任何改动,第一个I/O驱动程序写完后就可以进行实际的端口测试了。Atem AVR的端口移植由我们自己完成,包括有效的设备驱动程序,一共只用了2小时。Zilog Z80的端口移植由第三方完成,也只用了一天。
我们已经描述了为内存受限系统而设计的Contiki操作系统。为了减小系统的大小,Contiki基于事件驱动内核。由于对于长时间运行的程序来说,很难使用事件驱动系统中的状态机编程。Contiki提供了运行时在事件驱动内核之上的抢占式多线程应用库,这个库是可选地被链接到应用中,即当应用明确说明需要多线程模型时才能链接。
一个正在运行的Contiki分为两部分:核心和可加载程序。核心由内核、一些类基本服务、语言运行时部分和支持的库组成。加载程序可以在运行时被独立地加载、卸载。共享功能以共享库的形式实现。服务可以被独立更新或者替换,也增加了系统灵活性。
我们可以看出,在保持基本系统轻量级和紧凑的同时,做到了在资源受限系统上灵活地加载、卸载程序和服务。尽管我们的内核是基于事件驱动的,也为应用程序提供了可抢占式的多线程支持。
由于Contiki的动态特性,它可以被用于传感器网络的多硬件、多应用甚至多用户。我们将在支持安全机制方面继续我们的工作。
Contiki——为微传感器网络而生的轻量级的、灵活的操作系统
标签:
原文地址:http://blog.csdn.net/tidyjiang/article/details/51063215