1.6 Boot Sequence 學習筆記

Boot Sequence

From TinyOS Documentation Wiki

Contents

Introduction

一個在考慮 TinyOS而經常被問的問題是“ main() 在哪?”。在之前的課程裏,我們推遲了 TinyOS啓動順序的細節問題的討論:應用程序處理 Boot.booted事件和從那裏開始。此教程描述這個事件之前和之後的步驟,展示如何正確地初始化組件。

Boot Sequence

 

TinyOS啓動順序有 4步:

1.Scheduler initialization調度初始化

2.Component initialization 組件初始化

3.Signal that the boot process has completed 通知告訴 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個附加的接口, PlatformInit Schuduler MainC在應用程序中自動 wire它們到系統調度和平臺初始化順序中而隱藏了這些接口。 PlatformInit SoftwareInit之間的主要的不同是一個是硬件和軟件, 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步。

 

Scheduler Initialization

第一個啓動步驟是初始化調度。如果調度沒有在組件之前初始化,組件初始化應用將不能派發任務。並不是所有的組件都需要任務來派發,這就很自然的給出了那些組件需要那麼做了。啓動順序執行任務在初始化階段之後,爲了允許長時間的操作,因爲它們只發生一次。 TEP106詳細的描述了 TinyOS調度,包括如何去替換調度的信息。

 

Component initialization.

realMainP初始化調度之後,它初始化平臺。 Init接口僅執行一個單一的命令 init()

 

tos/interfaces/Init.nc:

interface Init {
  command error_t init();
}

平臺初始化階段負責平臺的執行。因此 PlatformInit被明確的平臺初始化組件 導通(wire) PlatformC。沒有別的組件需要 (導通)wire PlatformInit。任何組件需要初始化能執行 Init接口和 (導通)wire它自己到 MainC SoftwareInit接口:

 

 

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 initialization issues are handled directly by each platform's PlatformC component.明確的硬件初始化問題被每一個平臺的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 at radio initialisation time, rather it defers that action until the radio is started. In other words, initialisation is used to setup software 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 Init interface 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's Boot.booted() 事件被觸發。組件現在可以自由調用 start()和其他的命令在任何它們正在使用的組件上。從新看看 Blink應用程序, timer是從 booted()開始的。這個 booted事件是 Tinyos中類似於 Unix應用程序中的 main函數。

Once all initialization has completed, MainC 's Boot.booted() event is signaled. Components are now free to call start() and other commands on any components they are using. Recall that in the Blink application, the timers were started from the booted() event. This booted event is TinyOS's analogue of main in a Unix application.

Run the scheduler loop.

一旦應用程序已經通知了作爲 booted和啓動需要的服務的系統, Tinyos進入它的調度循環中。隊列中只要有任務調度就會運行。直到它檢測到一個空的隊列,調度將微控制器設置成能允許活動的硬件資源的低能耗狀態,例如,只有一個 timer頻繁運行允許低能耗狀態相對於不重要的 buss UART.TEP112詳細地描述了一個處理是如何運行的。

處理器進入睡眠狀態直到它處理到一箇中斷。當一箇中斷倒帶, MCU退出他的睡眠狀態運行中斷句柄。這就引起了調度循環從新啓動。如果中斷句柄派發了一個或更多的任務,調度執行任務指導任務隊列空然後從新返回到睡眠狀態。

 

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

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

Boot and SoftwareInit

從一個應用程序或者高級服務的角度來看,在 boot順序裏 2個重要的接口是那些 MainC輸出的: Boot SoftwareInit.Boot通常僅被高級應用程序處理:它啓動像 timer或者 radio的服務。 SoftwareInit與此相反,接觸系統的很多不同的部分。如果一個組件需要編碼來運行一次來初始化它的狀態或者配置,那麼它就可以通告( wire SoftwareInit

通常,服務組件需要初始化 wire它們自己來 SoftwareInit,這優於依賴應用程序作者來做這些。當一個應用程序開發者正在寫一個巨大複雜的系統,一直跟蹤着所有的初始化會是十分困難的,調試一個沒有正被運行的調用會是十分困難的。爲了避免 bug和簡化應用程序的開發,服務通常使用 auto-wiring

術語 auto-wiring涉及到當一個組件自動通告它的依賴相對於輸出它們對於一個應用程序作者來解決。在這個用例裏,到不如提供 Init接口,一個服務組件爲 RealMain wire它的 Init接口。例如, PoolC是一個一般的抽象內存池,它允許你能爲動態分配聲明一種內存對象的保護。 在下面,它的執行 (PoolP)需要初始化它的數據結構。表示這個必須能讓組件正確的操作,一個應用程序作者不應該去當心它。所以 PoolC組件實例化 PoolP wire它到 MainC.SoftwareInit

 

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

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

The term auto-wiring refers to when a component automatically wires its dependencies rather than export them for the application writer to resolve. In this case, rather than provide the Init interface, a service component wires its Init interface to RealMainC. For example, PoolC is a generic memory pool abstraction that allows you to declare a collection of memory objects for dynamic allocation. Underneath, its implementation (PoolP ) needs to initialize its data structures. Given that this must happen for the component to operate properly, an application writer shouldn't have to worry about it. So the PoolC component instantiates a 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 typical large application, the initialization sequence might involve as many as thirty components. But the application developer doesn't have to worry about this: properly written components take care of it automatically.

Related Documentation

Programming Hint 8: In the top-level configuration of a software abstraction, auto-wire Init to MainC. This removes the burden of wiring Init from the programmer, which removes unnecessary work from the boot sequence and removes the possibility of bugs from forgetting to wire. From TinyOS Programming


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章