1.6 Boot Sequence 学习笔记

来源:互联网 发布:淘宝能评论几次 编辑:程序博客网 时间:2024/05/19 11:47

Boot Sequence

From TinyOS DocumentationWiki

Contents

  • 1Introduction

  • 2Boot Sequence

    • 2.1Scheduler Initialization

    • 2.2Component initialization.

    • 2.3Signal that the boot process has completed.

    • 2.4Run the scheduler loop.

  • 3Boot and SoftwareInit

  • 4Related Documentation

Introduction

一个在考虑TinyOS而经常被问的问题是“main()在哪?”。在之前的课程里,我们推迟了TinyOS启动顺序的细节问题的讨论:应用程序处理Boot.booted事件和从那里开始。此教程描述这个事件之前和之后的步骤,展示如何正确地初始化组件。

Boot Sequence

 

TinyOS启动顺序有4步:

1.Scheduler initialization调度初始化

2.Component initialization 组件初始化

3.Signal that the boot process hascompleted 通知告诉boot过程已经完成

4.Run the scheduler 运行调度

应用程序级的启动顺序是MainC(在tos/system)。MainC提供一个接口Boot和使用一个接口Init作为SoftwareInit。启动顺序调用SoftwareInit()作为第二步的一部分和通知在第三步中的Boot.booted

默认的实际的启动顺序是在组件中的RealMainP。注意,它的标示是以P结束,代表此组件不能直接wire它。如下是RealMainP的形式:

 

  module RealMainP {    provides interface Booted;    uses {      interface Scheduler;      interface Init as PlatformInit;      interface Init as SoftwareInit;    }  }

MainC仅仅提供Boot和使用SoftwareInit;RealMainP使用2个附加的接口,PlatformInitSchudulerMainC在应用程序中自动wire它们到系统调度和平台初始化顺序中而隐藏了这些接口。PlatformInitSoftwareInit之间的主要的不同是一个是硬件和软件,PlatformInit负责将核心平台服务转入有意义的状态;例如,mica平台的PlatforInit标准化它们的时钟。

这里是RealMainP的代码:

 

 

/**

 * RealMain implements the TinyOS boot sequence, as documented in TEP 107.

 *

 * @author Philip Levis

 * @date   January 17 2005

 */

 

module RealMainP @safe() {

  provides interface Boot;

  uses interface Scheduler;

  uses interface Init as PlatformInit;

  uses interface Init as SoftwareInit;

}

implementation {

  int main() @C() @spontaneous() {

    atomic

      {

/* First, initialize the Scheduler so components can post

  tasks. Initialize all of the very hardware specific stuff, such

  as CPU settings, counters, etc. After the hardware is ready,

  initialize the requisite software components and start

  execution.*/

platform_bootstrap();

call Scheduler.init(); 

 

/* Initialize the platform. Then spin on the Scheduler, passing

* FALSE so it will not put the system to sleep if there are no

* more tasks; if no tasks remain, continue on to software

* initialization */

call PlatformInit.init();    

while (call Scheduler.runNextTask());

 

/* Initialize software components.Then spin on the Scheduler,

* passing FALSE so it will not put the system to sleep if there

* are no more tasks; if no tasks remain, the system has booted

* successfully.*/

call SoftwareInit.init(); 

while (call Scheduler.runNextTask());

      }

 

    /* Enable interrupts now that system is ready. */

    __nesc_enable_interrupt();

 

    signal Boot.booted();

 

    /* Spin in the Scheduler */       

    call Scheduler.taskLoop();

 

    /* We should never reach this point, but some versions of

     * gcc don't realize that and issue a warning if we return

     * void from a non-void function. So include this. */

    return -1;

  }

 

  default command error_t PlatformInit.init() { return SUCCESS; }

  default command error_t SoftwareInit.init() { return SUCCESS; }

  default event void Boot.booted() { }

}

 

代码显示了如上描述的4步。

 

SchedulerInitialization

第一个启动步骤是初始化调度。如果调度没有在组件之前初始化,组件初始化应用将不能派发任务。并不是所有的组件都需要任务来派发,这就很自然的给出了那些组件需要那么做了。启动顺序执行任务在初始化阶段之后,为了允许长时间的操作,因为它们只发生一次。TEP106详细的描述了TinyOS调度,包括如何去替换调度的信息。

 

Componentinitialization.

realMainP初始化调度之后,它初始化平台。Init接口仅执行一个单一的命令init()

 

tos/interfaces/Init.nc:interface Init {  command error_t init();}

平台初始化阶段负责平台的执行。因此PlatformInit被明确的平台初始化组件 导通(wire)PlatformC。没有别的组件需要(导通)wirePlatformInit。任何组件需要初始化能执行Init接口和(导通)wire它自己到MainCSoftwareInit接口:

 

 

tos/system/MainC.nc:configuration MainC {  provides interface Boot;  uses interface Init as SoftwareInit;}implementation {  components PlatformC, RealMainP, TinySchedulerC;  RealMainP.Scheduler -> TinySchedulerC;  RealMainP.PlatformInit -> PlatformC;  // Export the SoftwareInit and Booted for applications  SoftwareInit = RealMainP.SoftwareInit;  Boot = RealMainP;} 

一般的初始化问题都依赖于系统的不同部分。在TinyOS做如下的3个方面的处理:
  • Hardware-specific initializationissues are handled directly by each platform's PlatformCcomponent.明确的硬件初始化问题被每一个平台的PlatformC组件直接处理掉了。

  • System services (e.g., the timer,the radio) are typically written to be independently initializable.For instance, a radio that uses a timer does not setup the timer atradio initialisation time, rather it defers that action until theradio is started. In other words, initialisation is used to setupsoftware state, and hardware state wholly owned by the service.系统服务(例如,timer,radio)通常写成独立初始化的。例如,一个radio使用timer,在radio初始化时间里并没有安转timer,不同于action直到radio开始了。换句话说,初始化通常被用来设置软件状态,和硬件状态完全被服务拥有。

  • When a service is split into several components, the Initinterface for one of these components may well call Init(and other) interfaces of the other components forming the service,if a specific order is needed.当一个服务被分成几个组件时,如果一个明确的顺序需要的话,对于这些组件中的一个初始化接口可能会调用Init接口(和别的)其他的来自服务的组件接口。

Signal that the boot process has completed.

一旦所有的初始化都已完成了,MainC'sBoot.booted()事件被触发。组件现在可以自由调用start()和其他的命令在任何它们正在使用的组件上。从新看看Blink应用程序,timer是从booted()开始的。这个booted事件是Tinyos中类似于Unix应用程序中的main函数。

Once all initialization has completed, MainC'sBoot.booted() event is signaled.Components are now free to call start()and other commands on any components they are using. Recall that inthe Blink application, the timers werestarted from the booted() event. Thisbooted event is TinyOS's analogue ofmain in a Unix application.

Run thescheduler loop.

一旦应用程序已经通知了作为booted和启动需要的服务的系统,Tinyos进入它的调度循环中。队列中只要有任务调度就会运行。直到它检测到一个空的队列,调度将微控制器设置成能允许活动的硬件资源的低能耗状态,例如,只有一个timer频繁运行允许低能耗状态相对于不重要的bussUART.TEP112详细地描述了一个处理是如何运行的。

处理器进入睡眠状态直到它处理到一个中断。当一个中断倒带,MCU退出他的睡眠状态运行中断句柄。这就引起了调度循环从新启动。如果中断句柄派发了一个或更多的任务,调度执行任务指导任务队列空然后从新返回到睡眠状态。

 

Once the application has been informed that the system as bootedand started needed services, TinyOS enters its core scheduling loop.The scheduler runs as long as there are tasks on the queue. As soonas it detects an empty queue, the scheduler puts the microcontrollerinto the lowest power state allowed by the active hardware resources.For example, only having timers running usually allows a lower powerstate than peripheral buses like the UART. TEP112 describes in detail how this process works.

The processor goes to sleep until it handles an interrupt. When aninterrupt arrives, the MCU exits its sleep state and runs theinterrupt handler. This causes the scheduler loop to restart. If theinterrupt handler posted one or more tasks, the scheduler runs tasksuntil the task queue and then returns to sleep.

Boot andSoftwareInit

从一个应用程序或者高级服务的角度来看,在boot顺序里2个重要的接口是那些MainC输出的:BootSoftwareInit.Boot通常仅被高级应用程序处理:它启动像timer或者radio的服务。SoftwareInit与此相反,接触系统的很多不同的部分。如果一个组件需要编码来运行一次来初始化它的状态或者配置,那么它就可以通告(wireSoftwareInit

通常,服务组件需要初始化wire它们自己来SoftwareInit,这优于依赖应用程序作者来做这些。当一个应用程序开发者正在写一个巨大复杂的系统,一直跟踪着所有的初始化会是十分困难的,调试一个没有正被运行的调用会是十分困难的。为了避免bug和简化应用程序的开发,服务通常使用auto-wiring

术语auto-wiring涉及到当一个组件自动通告它的依赖相对于输出它们对于一个应用程序作者来解决。在这个用例里,到不如提供Init接口,一个服务组件为RealMainwire它的Init接口。例如,PoolC是一个一般的抽象内存池,它允许你能为动态分配声明一种内存对象的保护。在下面,它的执行(PoolP)需要初始化它的数据结构。表示这个必须能让组件正确的操作,一个应用程序作者不应该去当心它。所以PoolC组件实例化PoolPwire它到MainC.SoftwareInit

 

From the perspective of an application or high-level services, thetwo important interfaces in the boot sequence are those which MainCexports: Boot and SoftwareInit. Boot is typically only handled by thetop-level application: it starts services like timers or the radio.SoftwareInit, in contrast, touches many difference parts of thesystem. If a component needs code that runs once to initialize itsstate or configuration, then it can wire to SoftwareInit.

Typically, service components that require intialization wirethemselves to SoftwareInit rather than depend on the applicationwriter to do so. When an application developer is writing a large,complex system, keeping track of all of the initialization routinescan be difficult, and debugging when one is not being called can bevery difficult. To prevent bugs and simplify application development,services typically use auto-wiring.

The term auto-wiring refers to when a component automaticallywires its dependencies rather than export them for the applicationwriter to resolve. In this case, rather than provide the Initinterface, a service component wires its Init interface to RealMainC.For example, PoolC is a generic memory poolabstraction that allows you to declare a collection of memory objectsfor dynamic allocation. Underneath, its implementation (PoolP)needs to initialize its data structures. Given that this must happenfor the component to operate properly, an application writershouldn't have to worry about it. So the PoolC component instantiatesa PoolP and wires it to MainC.SoftwareInit:

generic configuration PoolC(typedef pool_t, uint8_t POOL_SIZE) {  provides interface Pool;}implementation {  components MainC, new PoolP(pool_t, POOL_SIZE);  MainC.SoftwareInit -> PoolP;  Pool = PoolP;}

实际上,这意味着当MainP调用SoftwareInit.init,它在很多的组件上调用Init.init。在一些通常的大应用程序里,初始化顺序可能需要和30个组件一样多。但是应用程序开发者不需要当心这个:正确的已经写好的组件会自动考虑它的。

In practice, this means that when MainP calls SoftwareInit.init,it calls Init.init on a large number of components. In a typicallarge application, the initialization sequence might involve as manyas thirty components. But the application developer doesn't have toworry about this: properly written components take care of itautomatically.

RelatedDocumentation

  • TEP106: Schedulers and Tasks

  • TEP107: Boot Sequence

Programming Hint 8: In the top-level configuration of asoftware abstraction, auto-wire Init to MainC. This removes theburden of wiring Init from the programmer, which removes unnecessarywork from the boot sequence and removes the possibility of bugs fromforgetting to wire. From TinyOSProgramming


 

原创粉丝点击