標籤: 暫無標籤

nesC 是對 C 的擴展,它基於體現TinyOS 的結構化概念和執行模型而設計 。 TinyOS 是為感測器網路節點而設計的一個事件驅動的操作系統,感測器網路節點擁有非常有限的資源 ( 舉例來說., 8K 位元組的程序儲存器,512個位元組的隨機存取儲存器) 。TinyOS 用 nesC 重新編寫。

 

1 nesC -簡介

 

       nesC 是對 C 的擴展,它基於體現TinyOS 的結構化概念和執行模型而設計 。 tinyos 是為感測器網路節點而設計的一個事件驅動的操作系統,感測器網路節點擁有非常有限的資源 ( 舉例來說., 8K 位元組的程序儲存器,512個位元組的隨機存取儲存器) 。TinyOS 用 nesC 重新編寫。

 

 

2 nesC -基本概念

 

   1  結構和內容的分離: 程序有組件構成, 它們裝配在一起 (" 配線 ") 構成完整程序. 組件定義兩類域, 一類用於它們的描述 ( 包含它們的介面請求名稱) ,另一類用於它們的補充。組件內部存在作業形式的協作。 控制線程可以通過它的介面進入一個組件。這些線程產生於一件作業或硬體中斷。
   2  根據介面的設置說明組件功能。 介面可以由組件提供或使用。被提供的介面表現它為使用者提供的功能,被使用的介面表現使用者完成它的作業所需要的功能。
   3  介面有雙向性: 它們敘述一組介面供給者 (指令)提供的函數和一組被介面的使用者(事件)實現的函數。這允許一個單一的介面能夠表現組件之間複雜的交互作用 (舉例來說.,當某一事件在一個回調之前發生時,對一些事件的興趣登記)。 這是危險的,因為 TinyOS 中所有的長指令 (舉例來說. 發送包)是非中斷的; 他們的完成由一個事件( 發送完成)標誌。 通過敘述介面,一個組件不能調用發送指令除非它提供 sendDone 事件的實現。通常指令向下調用,比如, 從應用組件到那些比較靠近硬體的調用,而事件則向上調用。特定的原始事件與硬體中斷是關聯的 (這種關聯是由系統決定的,因此在本參考手冊中不作進一步描述)。
    4   組件通過介面彼此靜態地相連。 這增加運行時效率,支持 rubust 設計, 而且允許更好的程序靜態分析。
    5   nesC基於由編譯器生成完整程序代碼的需求設計。這考慮到較好的代碼重用和分析。這方面的一例子是 nesC 的編譯-時間數據競爭監視器。
    6   nesC 的協作模型基於一旦開始直至完成作業 , 並且中斷遠源可以彼此打斷作業. nesC 編譯器標記由中斷源引起的潛在的數據競爭。

 

 

3 nesC -語法介紹

 

1  介面
nesC 的介面有雙向性: 它們描述一個多功能的兩組件(供給者和使用者)之間的交互渠道.。 介面敘述一組叫做指令的, 被介面的供給者實現的,被命名的功能和一組叫做事件的, 被介面的使用者實現.的,被命名的功能。
本節解釋介面如何被指定, 第 5 節解釋組件如何描述它們提供和使用的介面, 第 6 節解釋在 C代碼中指令和事件如何被調用和實現,而第 7 節解釋組件介面如何被一起聯編.
介面被介面類型指定,如下:
 
nesC-file:
 includes-listopt interface
 . . .
interface:
 interface identifier { declaration-list }
storage-class-specifier: also one of
 command event async
 
這聲明介面類型標識符. 這一標識符有全局的作用範圍並且屬於分開的命名空間,組件和介面類型命名空間。 如此所有介面類型都有清楚的名字以區別於其它介面和所有組件, 同時能不和一般的 C的聲明發生任何衝突。
聲明列表中,每個介面類型都有一個分開的聲明範圍。聲明列表必須由有指令或事件存儲類型的功能描述組成( 否則, 會發生編譯-時間錯誤). 可選的 async 關鍵字指出指令或事件能在一個中斷處理者中被運行。
通過包含列表,一個介面能可選擇地包括 C 文件 (見第9節)。
一個簡單的介面如下:
 
 interface sendmsg {
 command result_t send(uint16_t address, uint8_t length, TOS_MsgPtr msg);
 event result_t sendDone(TOS_MsgPtr msg, result_t success);
 }
 
SendMsg 介面類型提供者必須實現發送指令, 而使用者必須實現 sendDone 事件. 

2  組件說明
一個 nesC 組件或是一個模塊 (第 6 節) 或一個結構 (第 7 節):
nesC-file:
 includes-listopt module
 includes-listopt configuration
 . . .
module:
 module identifier specification module-implementation
configuration:
 configuration identifier specification configuration-implementation
組件的名字由標識符指定. 這一標識符有全局的作用範圍並且屬於組件和介面類型命名空間. 一個組件介入兩個分組件的作用域::一個規格作用域,屬於 C 中全局的作用域,和一個實現作用域屬於規格作用域。
通過包含列表,一個組件能可選擇地包括 C 文件 (見第9節).
組件規格列出該組件提供或使用的規格元素 (介面請求,指令或事件)。 就如我們在第 4 節中見到的,一個組件必須實現它提供介面的指令和它的使用的介面事件。另外,它必須實現它提供的指令和事件。
典型地,指令向下調用硬體組件,而事件向上調用應用組件 (這表現為nesC應用如一個應用組件處於頂端的組件曲線圖)。 一個控制線程只有通過它的規格元素越過組件。
每種規格元素有一個名字 (介面實例名,命令名或事件名).這些名字屬於總組件-規格作用域的變數命名空間。
 specification:
 { uses-provides-list }
 uses-provides-list:
 uses-provides
 uses-provides-list uses-provides
 uses-provides:
 uses specification-element-list
 provides specification-element-list
 specification-element-list:
 specification-element
 { specification-elements }
 specification-elements:
 specification-element
 specification-elements specification-element
一個組件說明中可以有多個使用和提供指令。多個使用和提供規格元素可以通過包含在{ and}中而組合在一個指令中。舉例來說,下面兩個說明是一樣的:
 module A1 {                            module A1 {
 uses interface X;                     uses {
 uses interface Y;                             interface X;
} ...                                                        interface Y;
 }
 } ...
一個介面實例描述如下:
 specification-element:
 interface renamed-identifier parametersopt
 . . .
 renamed-identifier:
 identifier
 identifier as identifier
 interface-parameters:
 [ parameter-type-list ]
 
介面實例聲明的完整語法是 interface X as Y,明確地指明Y作為介面的名字。interface X是interface X as X.的一個速記.
如果介面-叄數被省略, 那麼interface X as Y聲明一個簡單的介面實例,對應這一組件的一個單一介面。 如果介面-叄數是給出的 (舉例來說., interface SendMsg S[uint8 t id]) ,那麼就是一個參量介面實例聲明,對應這一組件的多個介面, 每個介面對應不同參數值(因此interface SendMsg S[uint8 t id]聲明SendMsg類型的 256個介面). 叄數的類型必須是完整的類型 (這裡enums 是不允許的).指令或事件能通過包括一個聲明了指令或事件及存儲類型的標準的 C函數而作為規格元素直接地被包含:
 specification-element:
 declaration
 . . .
 storage-class-specifier: also one of
 command event async
如果該聲明不是帶有指令或事件存儲類型的函數聲明就會產生一編譯- 時間錯誤。在介面中, 文法指出指令或事件能被一中斷操縱者運行。
作為介面實例, 如果沒有指定介面叄數,指令 (事件)就是簡單的指令 (簡單的事件),如果介面叄數是指定的,就是參數化指令 (參數事件)。介面叄數被放置在一般的函數叄數列表之前,舉例來說.,
command void send[uint8 t id](int x):
 direct-declarator: also
 direct-declarator interface-parameters ( parameter-type-list )
 . . .
注意介面叄數只在組件說明裡面指令或事件上被允許, 而不允許在介面類型裡面.
這兒有一個完整的規格例子:
 configuration GenericComm {
 provides {
 interface StdControl as Control;
 // 該介面以當前消息序號作參數
 interface SendMsg[uint8_t id];
 interface ReceiveMsg[uint8_t id];
 }
 uses {
 //發送完成之後為組件作標記
 //重試失敗的發送
 event result_t sendDone();
 }
 } ...
在這個例子中,一般:
           提供簡單的介面實例類型 StdControl 的控制.
           提供介面類型 SendMsg 和 ReceiveMsg 的參數實例; 參數實例分別地叫做   SendMsg 和 ReceiveMsg.
           使用事件 sendDone.
我們說,在組件 K 的規格中提供的一個指令 (事件) F 是K的提供指令(事件) F; 同樣地,一個被用於組件 K 的規格的指令 (事件) 是K 的使用指令 (事件) F。
組件K的提供介面實例X的指令F是K的提供指令X.F;組件K的使用介面實例X的指令F是K的使用指令X.F 。K的提供介面實例X中的事件F是K的使用事件X.F; K的使用介面實例X中的事件F是K的提供事件X.F ( 注意事件的使用和提供根據介面雙向屬性的顛倒)。
當使用/提供區別關係不大時,我們常常只簡單的提到" K的指令或事件a"。K的指令或事件a可能是參數化的或簡單的, 取決於其通信的規格元素的參數化或簡單狀態.


3   模塊
模塊用C代碼實現組件說明:
 module-implementation:
 implementation { translation-unit }
 
這裡編譯基本單位是一連串的 C 聲明和定義 ( 見K& R[2 , pp234 –239])。
模塊編譯基本單位的頂層聲明屬於模塊的組件說明域。這些聲明的範圍是模糊的而且可以是: 任意的標準 C聲明或定義,一種作業聲明或定義,指令或事件實現. 


 實現模塊的說明
編譯基本單位必須實現模塊的所有的提供指令 (事件)a (例如., 所有的直接提供指令和事件, 以及提供介面的所有指令和使用介面的所有事件). 一個模塊能調用它的任一指令和它的任一事件的信號.
這些指令和事件的實現由如下的 C 語法擴展指定:
 storage-class-specifier: also one of
 command event async
 declaration-specifiers: also
 default declaration-specifiers
 direct-declarator: also
 identifier . identifier
 direct-declarator interface-parameters ( parameter-type-list )
 
簡單指令或事件a由帶有存儲類型指令或事件的C 函數定義的語法實現 (注意允許在函數名中直接定義的擴展)。另外,語法關鍵字必須被包含如果它被包含在a的聲明中。舉例來說,在SendMsg類型的提供介面Send的模塊中:
 command result_t Send.send(uint16_t address, uint8_t length, TOS_MsgPtr msg) {
 ...
 return SUCCESS;
 }
帶有介面參數P的參數指令或事件a,由帶有存儲類型指令或事件的函數定義的C文法實現,這時,函數的普通參數列表要以P作為前綴,並帶上方括弧 ( 這與組件說明中聲明參數化指令或事件是相同的文法)。這些介面叄數聲明P 屬於a的函數叄數作用域而且和普通的函數參數有相同的作用域。舉例來說,在SendMsg類型提供介面Send[uint8 tid]的模塊中:
 command result_t Send.send[uint8_t id](uint16_t address, uint8_t length,
 TOS_MsgPtr msg) {
 ...
 return SUCCESS;
 }
以下情況將報告編譯- 時間錯誤:
          提供指令或事件沒有實現。
          類型標誌,可選擇的介面叄數和指令或事件語法關鍵字的存在或缺失,或與模塊說明不匹配 


 調用命令和事件信號
對 C 語法的下列擴展用於調用事件和向指令發出信號:
 postfix-expression:
 postfix-expression [ argument-expression-list ]
 call-kindopt primary ( argument-expression-listopt )
 . . .
 call-kind: one of
 call signal post
 
一個簡單的指令a使用call _(...)調用, 一件簡單的事件使用signal a(...)發送訊號。舉例來說,在一個模塊中使用SendMsg類型介面Send:call Send.send(1,sizeof(Message), &msg1)。
一個參數指令a(個別地,一件事件)有 n個介面叄數,類型為t1 , . . . , t n由介面參數表達式e1 , . . . ,en調用如下:call _[e1, . . . , en](...) (個別地,signal _[e1, . . . , en](...))。介面叄數表達式 ei 必須分配類型t i; 實際的介面叄數值是ei影射到t i. 舉例來說, 在一個組件中使用類型 SendMsg 的介面Send[uint8 t id]:
 int x = ...;
 call Send.send[x 1](1, sizeof(Message), &msg1);
 
指令和事件的執行是立即的,也就是,調用和發送信號行為和函數調用是同樣地。實際的指令或事件是由調用還是信號表達運行取決於程序結構聯繫說明。 這些聯繫說明可能指定0,1 或更多的實現將被運行。當超過 1個實現被運行, 我們說模塊的指令或事件為"扇出"。
一個模塊能為一使用指令或事件a指定默認的調用或信號實現。提供指令或事件的默認實現會引起編譯-時間錯誤。如果a未與任何指令或事件實現聯繫,默認的實現將被執行。默認的指令或事件由帶有默認關鍵字的指令或事件實現前綴定義:
 declaration-specifiers: also
 default declaration-specifiers
 
舉例來說, 在一個類型 SendMsg使用介面Send的模塊中:
 default command result_t Send.send(uint16_t address, uint8_t length,
 TOS_MsgPtr msg) {
 return SUCCESS;
 }
 "
 ... call Send.send(1, sizeof(Message), &msg1) ... 
 
原子的陳述 
 atomic-stmt:
 atomic statement
確保陳述被運行 " 好像 " 沒有其它的運算同時發生。它用於更新併發的數據結構的互斥變數,等等。 一簡單的例子是:
 bool busy; //全局
 void f() {
 bool available;
 atomic {
 available = !busy;
 busy = TRUE;
 }
 if (available) do_something;
 atomic busy = FALSE;
 }
 
原子的區段應該很短, 雖然這常常並不是必須的。控制只能" 正常地 " 流入或流出原子的陳述: 任何的 goto, break或continue,跳轉入或出一原子陳述都是錯誤的。返回陳述決不允許進入原子陳述。


結構
結構通過連接,或配線,集合其他組件實現一個組件說明:
 configuration-implementation:
 implementation { component-listopt connection-list }
 
組件列表列出用來建立這一個結構的組件,連接列表指明各組件之間,以及與結構說明之間是怎樣裝配在一起的。在這一節的其餘部分中,我們調用來自結構的外部的規格元素
, 和來自結構的內在的成份之一的規格元素。 


 包含組件
組件列表列出用來建立這一個結構的組件。在結構裡面這些組件可隨意的重命名,使用共同外形規格元素,或簡單的改變組件結構從而避免名稱衝突。(以避免必須改變配線)為組件選擇的名字屬於成份的實現域。
 component-list:
 components
 component-list components
 components:
 components component-line ;
 component-line:
 renamed-identifier
 component-line , renamed-identifier
 renamed-identifier:
 identifier
 identifier as identifier
 
如果二個組件使用as給出相同的名字,則會發生編譯時間錯誤(舉例來說., components
X, Y as X)。
只有一個個別的例子:如果組件 K 被用於二不同的結構 ( 或甚至兩次用於相同的結構裡面), 在程序中仍然只有 K(及它的變數) 的唯一實例。


配線
配線用於連接規格元素 (介面,指令,事件)。本節和下一節(第 7.3 節) 定義配線的語法和編譯-時間規則。第 7.4 節詳細說明程序配線聲明是如何指出在每個調用和信號表達中哪個函數被調用。
 connection-list:
 connection
 connection-list connection
 connection:
 endpoint = endpoint
 endpoint -> endpoint
 endpoint endpoint2:( 聯編配線) 一個連接包括二種內在的規格元素。.聯編配線總是連結一由endpoint1指定的使用規格元素到一endpoint2指定的提供規格元素。如果這兩個條件不能滿足, 就會發生編譯-時間錯誤.。
          endpoint1 endpoint1是等價的。
在配線的所有三種類型中,兩被指定的規格元素必須是一致的,就是說., 它們必須都是指令,或都是事件, 或都是介面實例. 同時, 如果它們是指令(或事件),則它們必須有相同的函數名 如果他們是介面實例,它們必須有相同的介面類型。他們一定是有相同的介面類型的. 如果這些條件不能滿足, 就會發生編譯-時間錯誤.。
如果一個端點是參數化的,則另一個必須也是而且必須有相同的叄數類型;否則就會發生編譯-時間錯誤.。
相同的規格元素可以被多次連接,舉例來說.,:
 configuration C {
 provides interface X;
 } implementation {
 components C1, C2;
 X = C1.X;
 X = C2.X;
 }
 
在這個例子中,當介面X中的命令被調用時,多次的配線將會導致介面X的事件的多重信號 ("扇入"),以及多個函數的執行("扇-出")。注意,當二個結構獨立地聯結相同介面的時候,多重配線也能發生,舉例來說.:
 configuration C { }                configuration D { }
 implementation {                  implementation {
 components C1, C2;                components C3, C2;
 C1.Y -> C2.Y;                    C3.Y -> C2.Y;
 }                                }
 
所有的外部規格元素必須配線,否則發生編譯-時間錯誤. 可是,內部的規格元素可以不連接 (它們可能在另外一個結構中配線,或者如果模塊有適當的默認事件或指令實現,他們可以不配線).

 


隱含連接
隱含連接可以寫成K1 是等價的). 該用法通過規格元素K1 (不妨K2)來引用規格元素 Y,因此K1.Y  M.P;
 h2 = M.h;
 }
 圖 1: 簡單的配線例子
 components M1, M2;
 M2.SC -> M1;
 }
 
M2.SC -> M1 這一行與M2.SC -> M1.StdControl. 是等價的。


配線語義
我們首先撇開參數化介面討論配線語義. 7.4.1 節將討論參數化介面。最後,第 7.4.2 節敘述整體上而言,程序配線聲明上的要求。我們將會用到圖1中的簡單程序作為我們運行的例子。
我們根據中間函數定義配線的意義。每個組件的每個指令或事件都有中間函數. 舉例來說,在圖 1 中,模塊M 有中間函數 IM.P.f , IM.P.g , IM.U.f , IM.U.g , IM.h. 在例子中,我們以其組件,任意介面實例名,及函數名為基礎命名中間函數。中間函數不是使用就是提供。每個中間函數接受與組件說明中相應指令或事件相同的自變數。中間函數體I是調用(執行系列)其它中間函數的列表。I 通過程序配線說明連接到其它中間函數 。I接受的自變數不變的經過被調用的中間函數.I 返回結果列表,(列表元素類型是相應指令或事件返回給I的結果類型),列表通過連接調用中間函數返回結果構成。返回空值的中間函數適合不相
連接的指令或事件;返回兩個或以上值的中間函數適合「扇出」。

中間函數
中間函數和結構 一個結構的配線說明指定中間函數體。我們首先擴展配線說明到中間函數而不限於規格元素,並取消配線說明中= 和->的區別。我們用 I1 I2 表示中間函數I1 和I2之間的連結。舉例來說,圖 1中的結構C 敘述了下列中間函數連接:
IC.X.f IM.P.f     IM.U.f IM.P.f     IC.h2IM.h
IC.X.g IM.P.g     IM.U.gIM.P.g
在結構 C 的連接I1  I2中,二個中間函數之一是被調用的,另一個是調用者。如果下列任一條件成立(我們使用內部或外部的用辭作規格說明並不妨礙結構 C包含連接),則I1(同樣地,I2)是被調用的:
          如果 I1 符合一件被提供指令或事件的內部規格元素.
          如果 I1 符合一件被使用指令或事件的外部規格元素.
          如果 I1 符合一個介面實例X 的指令,而X是內部的且被提供或外部的且被使用的規格元素.
          如果 I1 符合一個介面實例X 的事件,而X是外部的且被提供或內部的且被使用的規格元素.
如果這些情況沒有一個成立,則I1 調用者。7.2 節的配線規則確保一個連接 I1I2 不會同時連接二個調用者或二個被調用者。圖1的結構 C 中,IC.X.f , IC.h2 , IM.P.g,IM.U.f 是調用者而 IC.X.g , IM.P.f , IM.U.g,IM.h 是被調用者。如此C的連接說明IC.X.f 調用IM.P.f,IM.P.g調用IC.X.g,等等。
中間函數和模塊  模塊中的C代碼調用中間函數,或被中間函數調用。
模塊M中提供指令或事件a的中間函數I 包含一個單獨調用以運行M中的a。其結果是
一個單獨的調用返回列表。表達式call a(e1, . . . , en)性質如下:
          自變數e1, . . . , en 被賦值為v1, . . . , vn.。
          a對應的中間函數被以自變數v1, . . . , vn調用,返回結果列表L.
          如果 L=(w)( 一個獨立列表),調用的返回結果就是 w.
如果 L=(w1,w2, . . . ,wm) (二或更多的元素),調用的結果仰賴於a的返回類型t。如果t=void,則結果是void。否則,t 一定有一聯合函數c( 第 10.3節演示聯合函數是如何聯合類型的),否則發生編譯-時間錯誤。聯合函數接受類型t 的兩個值並且返回一個類型t的結果。該調用的結果是c(w1, c(w2, . . . , c(wm−1,wm))) ( 注意L中元素次序是任意的).
 
 list of int IM.P.f() {              list of void IM.P.g(int x) {  
 return list(M.P.f());              list of int r1 = IC.X.g(x);
 }                               list of int r1 = IM.U.g(x);
 return list concat(r1, r2);
 }
 list of int IM.U.f() {             list of void IM.U.g(int x) {
  return IM.P.f();                  return list(M.U.g(x));
 }                           }
 list of int IC.X.f() {              list of void IC.X.g(int x) {
  return IM.P.f();                    return empty list;
 }                            }
 list of void IC.h2() {              list of void IM.h() {
 return IM.h();                    return list(M.h());
 }                            } 
如果 L 為空則默認以v1, . . . , vn,為自變數調用執行a,並返回該調用結果。第 7.4.2 節表明如果L為空且a沒有默認實現則會發生一編譯- 時間錯誤。
信號表達式的規則是一樣的。
 
中間函數舉例  圖 2使用類C的語法演示了圖 1中組件產生的中間函數,其中list(x)產生一個包含X的獨立列表,空列表是表示含0個元素的列表的常量,連接列表如鎖鏈般連接兩個列表。對M.P.f, M.U.g, M.h的調用,調用模塊M中實現的指令和事件(未給出)。


配線和參數化函數
如果組件K的一條指令或事件a帶有類型t1, . . . ,tn的介面叄數,則對每一數組(v1 : t1, . . . , vn :
tn)存在一個中間函數Ia,v1,...,vn 。
在模塊中,如果中間函數Iv1,...,vn符合參數化的提供指令(或事件)a,則Iv1,...,vn中對a的實現的調用將傳遞v1, . . . , vn作為a的介面參數。
下面是對表達式call _[e01, . . . , e0m](e1, . . . , en)討論:
          自變數e1, . . . , en被賦值為 v1, . . . , vn。
          自變數e01, . . . , e0m被賦值為v01 , . . . , v0m。
          v0i 對應ti 類型, 這裡t i 是a的第i個介面叄數的類型。
          a對應的中間函數Iv01 ,...,v0m被以參數v1, . . . , vn,調用,返回列表L。
          如果 L 有一個或更多的元素, 在非參數化的情形下產生調用結果
          如果 L 為空,a的默認實現會被以自變數v1, . . . , vn,,以介面參數值v01 , . . . , v0m調用,且返回該調用的結果。7.4.2節表明如果L為空且a沒有默認實現,則會產生編譯-時間錯誤。
信號表達式的規則是一樣的.
配線說明中的一個端點關係到一參數化規格元素時,有二種情形:
          端點指定叄數值 v1, . . . , vn。若端點符合指令或事件 a1, . . . ,am ,則相應的中間函數為Ia1,v1,...,vn,. . . , Iam,v1,...,vn且配線方式不變。
          端點未指定叄數值. 在這情況下,配線說明的兩個端點都對應相同介面參數類型t1, . . . ,tn.的參數化規格元素。 如果一個端點對應指令或事件 a1, . . . , am 而另一端點對應指令或事件 β1, . . . , βm,則對所有的1 Iβi,w1,...,wn (就是說., 端點是為所有對應的叄數值連接的).。
7.4.2 應用級的需求
一個應用的配線說明必須滿足兩個需求, 否則就會發生編譯時間錯誤:
          沒有隻包含中間函數的無限循環.
          在應用模塊中的每個call a ( 或signal a)表達式中:
 –如果調用是非參數化的:如果調用返回空的結果列表,則a一定有默認實現 (結果列表中元素個數只仰賴於配線)。
 –如果調用是參數化的:如果a的介面叄數的任何替代值都返回空結果列表,則a必定有默認的實現 (給定參數值數組的返回結果列表中元素數目只仰賴於配線)。
 注意這種情況不考慮用來在調用點敘述介面參數值的表達。 

nesC 的協作
nesC採用由一旦運行直至完成作業(代表性的實時運算)和硬體非同步觸發中斷控制構成的運行模型。編譯器依靠用戶提供的事件句柄和原語特徵來識別中斷源 (見10.3節)。nesC調度程序能以任意次序運行作業,但是必須服從一旦運行直至完成規則 (標準的TinyOS調度程序遵從FIFO(先進先出)策略).因為作業不能獨佔且是一旦運行直至完成的,所以它們是原子的互不妨礙的,但能夠被中斷。
由於這種并行運行模型,在程序共享的狀態下特殊數據競爭,導致nesC 程序狀態是不穩定的。比如,它的全局和模塊內變數 (nesC不含動態存儲配置). 為避免競爭,要麼只在作業內部訪問共享狀態,要麼只在原子的聲明內部訪問。編譯時,nesC 編譯器會報告潛在的數據競爭。
形式上, nesC 程序代碼分為二個部份:
同步碼 (SC):僅僅在作業內部可達的編碼 (函數,指令,事件,作業)
非同步碼 (AC):至少一個中斷源可達的代碼.
雖然非搶佔消除作業之間的數據競爭, 但是在SC 和 AC,以及AC 和 AC 之間仍然有潛在的競爭。通常,任何從 AC可達的共享狀態更新都是一個潛在的數據競爭. nesC 運行的基本常量是:
無競爭常量:任何共享狀態更新要麼僅同步碼可達,要麼僅發生在原子陳述內部. 只要所有對函數f 的調用是在原子陳述內部的,我們就認為對f 的調用是在原子陳述內部的。
這可能引入一種編譯器不能夠發現的競爭情況,但它一定是跨越多個原子陳述或作業的,並且是使用中間存儲變數的。
nesC 可能報告實際上不會發生的數據競爭,舉例來說., 如果所有的通路都被其他變數上的守衛保護。在這種情況下,為避免多於的消息,程序會用註釋存儲類型說明註釋一個變數v,從而忽略所有關於v的數據競爭警告。註釋關鍵字應謹慎使用。
對任何非同步碼的且沒有聲明非同步的指令或事件,nesC 會報告編譯- 時間錯誤。
這確保那些不安全的代碼不會在中斷時無意中被調用。


nesC 應用程序

一個 nesC應用程序有三個部份。:一連串的 C 聲明和定義,一組介面類型,和一組組件。nesC 應用程序命名環境構造如下:
   最外層的全局命名環境,包含三個命名域: 一個 C 變數,一個用於C聲明和定義的C 標籤命名域,和一個用於組件和介面類型的組件和介面類型命名域。
   通常,C聲明和定義可以在全局命名環境內部引入自己的嵌套命名域(用於函數聲明和定義的函數內部代碼段,等等)。
   每個介面類型引入一個命名域,用於保存介面的指令或事件。這種命名域是嵌套於全局命名環境的,所以指令和事件定義能影響全局命名環境中的C類型和標籤定義。
   每個組件引入二個新命名域。規格命名域,嵌套於全局命名環境,包含一變數命名域用於存放組件規格元素。實現命名域, 嵌套於規格命名域,包含一個變數和一個標籤命名域。
對於結構,作用範圍變數命名域包含組件用以引用其包含組件的名字 (7.1節). 對於模塊,作用範圍保存作業,以及模塊體中的C聲明和定義。這些聲明,及其它可能引入自己的嵌套在作用範圍內的命名域 (比如函數體,代碼段等等). 由於這種命名域的嵌套結構,模塊中的代碼可以訪問全局命名環境中的C聲明和定義,但是不能訪問其他組件中的任何聲明或定義.。
構成一個nesC應用程序的C聲明和定義,介面類型和組件由一個隨選的裝載程序決定。nesC 編譯器的輸入是一個單獨的組件K。nesC 編譯器首先裝載C文件 (第 9.1 節),然後裝載組件K(9.2節)。 程序所有代碼的裝載是裝載這兩個文件的過程的一部分。nesC 編譯器假定所有對函數,指令及事件的調用不以自然的屬性 (第 10.3 節) 都發生被裝載的代碼中(例如., 沒有對非自然的函數 " 看不見的 " 調用)。
在裝載文件預處理的時候,nesC 定義NESC 符號,用於識別nesC 語言和編譯器版本的數字 XYZ。對於nesC , XYZ 至少為110。
裝載C 文件,nesC組件及介面類型的過程包括定位對應的資源文件。文件定位的機制不是本參考手冊中所要討論的。要詳細了解通用編譯器是如何作業的,請閱讀《the ncc man page.》 
裝載 C文件X
如果 X 已經被裝載,就不用再做什麼。否則, 就要定位並預處理文件 X.h。C宏定義 ( 由 # define和 #undef) 的改變會影響到所有的後面的文件預處理。來自被預處理的文件X.h的 C聲明和定義會進入C全局命名環境,因此對所有的後來的 C文件加工,介面類型和組件是有影響的。 

 裝載組件K
如果K已經被裝載,就不用再做什麼。否則, 就要定位並預處理文件 X.nc。對C宏定義( 由 # define和 #undef)的變化被忽略。使用下面的語法分析預處理文件:
 nesC-file:
 includes-listopt interface
 includes-listopt module
 includes-listopt configuration
 includes-list:
 includes
 includes-list includes
 includes:
 includes identifier-list ;
 
如果 X.nc沒有定義模塊K 或結構 K,將報告編譯-時間錯誤。否則,所有的包含列表指定的C文件都將被裝載 (9.1節)。然後,組件說明中用到的所有介面類型都將被裝載(9.3節)。接著,處理組件說明(第5節). 如果 K 是一個結構, K 指定的 (第 7.1 節) 的所有組件被
裝載 (9.2節)。最後,K的實現被處理 (第6節和第7節)。


載入介面類型I
如果I已經被裝載,就不用再做什麼。否則, 就要定位並預處理文件 X.nc。對C宏定義( 由 # define和 #undef)的變化被忽略。預處理文件同上面的nesC-文件一樣分析。如果 X.nc沒有定義介面I,將報告編譯-時間錯誤。否則,所有的包含列表指定的C文件都將被裝載 (9.1節)。接著,處理I的定義(第4節).。
作為組件或介面包含C 文件的例子,介面類型Bar可能包含用於定義Bar中使用的類型的C文件BarTypes.h:
 Bar.nc:                                  BarTypes.h:
 includes BarTypes;                        typedef struct {
 interface Bar {                                int x;
 command result_t bar(BarType arg1);           double y;
 }                                      } BarType;
介面Bar的定義能參考Bar類型, 同樣任何使用和提供介面Bar組件也能(裝載任何這些組件說明或實現之前,都要先裝載介面Bar,自然還有BarTypes.h)


多樣性
沒有自變數的函數的C聲明的舊風格
沒有自變數的 nesC函數使用()聲明, 而不是 (void)。後者的用法將報告編譯-時間錯誤.。
舊式的C聲明(用())和函數定義(在自變數之後指定參數列表)在介面和組件中是不允許的(會引起編譯-時間錯誤)。
注意這些變化都不用於C文件(以便現有的.h 文件能被不變的使用).
註釋
nesC 允許C,介面類型和組件文件中的 //註釋 。 
屬性
nesC使用gcc的屬性語法聲明函數的一些屬性,變數及類型。這些屬性可以放置在聲明(在聲明符之後) 或函數定義.(在叄數列表之後)上。 x 的屬性是全部x 的聲明和定義.上的所有屬性的集合。
nesC 的屬性語法是:
 init-declarator-list: also
 init-declarator attributes
 init-declarator-list , init-declarator attributes
 function-definition: also
 declaration-specifiersopt declarator attributesdeclaration-listoptcompound-statement
 attributes:
 attribute
 attributes attribute
 attribute:
 attribute ( ( attribute-list ) )
 attribute-list:
 single-attribute
 attribute-list , single-attribute
 single-attribute:
 identifier
 identifier ( argument-expression-list )

nesC 支持五種屬性:
          C:這一屬性用於在一個模塊的頂層作為C 聲明或定義 d( 它被所有其他聲明忽略). 這指明d應該出現在全局範圍,而不是模塊的組件作用域。這允許在C代碼中使用(舉例來說,如果它是一個函數,則可被調用)d。
          自然的: 這一個屬性可用於任何函數 f (在模塊或 C代碼中)。.這指出對f的調用在源代碼中是不可見的。典型地,函數自然地被中斷源 ,和C主函數調用。第9節討論 nesC 編譯器在編譯期間如何使用自然的屬性。
          事件句柄: 這一個屬性可用於任何函數 f (在模塊或 C代碼中)。它指出f 是一個中斷處理函數, 自動被硬體調用。這意味著f 既是自然的又是非同步碼 (AC)。
          原子的事件句柄: 這一個屬性可用於任何函數 f (在模塊或 C代碼中)。它指出f 是一個中斷處理函數, 自動被硬體調用,屏蔽中斷的運行。這意味著f 既是自然的又是非同步碼 (AC)。而且,f 運行時好像被封裝進一個原子的陳述。
          聯合 (fnname): 這一屬性為類型定義聲明中一個類型指定聯合函數。聯合函數指定該如何聯合調用一指令或事件而"扇出「返回的多個結果。舉例來說:
 typedef uint8_t result_t __attribute__((combine(rcombine)));
 result_t rcombine(result_t r1, result_t r2)
 {
 return r1 == FAIL ? FAIL : r2;
 }
當聯合指令(或事件)返回類型是t 時,敘述邏輯-相似的行為。詳細的語義見第 7.4 節。
如果類型 t 的聯合函數c沒有類型t c(t,t),就會發生編譯時間錯誤。
使用屬性的例子:在文件 RealMain.td 中:
 module RealMain { ... }
 implementation {
 int main(int argc, char **argv) __attribute__((C, spontaneous)) {
 ...
 }
 }
這個例子表明主函數實際上應該出現在 C全局命名空間 (C),所以連接器能找它。它還表明即使在程序任何地方都沒有函數調用主函數,主函數同樣能夠被調用(自然的)。 


 編譯-時間常量函數
nesC有新類型的常量表達式:常量函數。常量函數在語言裡面定義的函數,編譯時當作一個常數.
nesC 現在有二種常量函數:
          unsigned int unique(char *identifier)
返回值:如果程序包含 n個有相同標示字元串的對unique的調用,每個
調用返回一個0— n-1之間的無符號整數。
有意使用unique是為了傳遞一個獨特的整數給參數化介面實例,以便一個組件只要提供一個參數化介面就能唯一地識別連接到那個介面的各種不同組件。
          unsigned int uniqueCount(char *identifier)
返回值:如果程序包含 n個有相同標示字元串的對uniqueCount的調用,每個調用都返回n。
有意使用uniqueCount是為了度量數組(或其他的數據結構),數組使用uniqueCount返回的數變址。
舉例來說, 一個定時器服務通過一個參數化介面識別它的客戶 (每個獨立的定時器由此而來)並且unique可以使用uniqueCount來分配正確個數的定時器數據結構。
附錄A 語法
讀本語法時,請翻閱Kernighan and Ritchie (K&R) [2, pp234–239]的附錄A。
下列關鍵字對 nesC 是新的: as, call, command, components, configuration, event,
implementation, interface, module, post, provides, signal, task, uses, includes. 這些關鍵字在 C 文件中是不被保留的。 對應的 C 符號,通過加上_nesc_keyword前綴(舉例來說.,_ nesc _keyword _as).,在nesC文件中是可用的。
nesC所有的標識符均以_nesc開頭保留作為內部使用。TinyOS 保留所有的標識符以TOS_和TOSH_開頭。
nesC文件遵循nesC文件要求; .h 文件通過includes包含,遵循來自 K&R 的指令編譯單位
新的規則:
nesC-file:
 includes-listopt interface
 includes-listopt module
 includes-listopt configuration
includes-list:
 includes
 includes-list includes
includes:
 includes identifier-list ;
interface:
 interface identifier { declaration-list }
module:
 module identifier specification module-implementation
module-implementation:
 implementation { translation-unit }
configuration:
 configuration identifier specification configuration-implementation
configuration-implementation:
 implementation { component-listopt connection-list }
component-list:
 components
 component-list components
components:
 components component-line ;
component-line:
 renamed-identifier
 component-line , renamed-identifier
renamed-identifier:
 identifier
 identifier as identifier
connection-list:
 connection
 connection-list connection
connection:
 endpoint = endpoint
 endpoint -> endpoint
 endpoint  identifier
 postfix-expression
 postfix-expression --
call-kind: one of
 call signal post

---------------------------------------

辭彙解釋


•聯合函數: 連接前一扇出中指令(或事件信號)調用的多個返回結果的C函數。
•指令, 事件: 一個函數,作為組件說明的一部分,它要麼直接地作為規格元素,要麼在組件的一個介面實例中。當直接作為規格元素時,指令和事件有自己的角色 (提供者 , 使用者),而且可以有介面叄數。而對介面時實例, 我們區分沒有介面參數的簡單指令(事件)和有介面參數的複雜指令(事件)。指令或事件的介面參數可以從它的常用函數叄數中了解。
•編譯- 時間錯誤: 一個 nesC 編譯器在編譯時必須報告的錯誤。
•組件: nesC 程序的基本單位。成份有名字並且有二個類型:模塊和結構。組件有說明和實現。
•結構: 一種組件,其實現由別的組件內容通過一特殊配線提供。
•端點: 在結構的配線陳述中的特別的規格元素的說明, 和可選地一些介面叄數值。參數化端點是沒有符合參數化規格元素的叄數值的端點。
•事件: 見指令.
•作用域: 變數的生存時間。nesC 有標準的 C作用域: 模糊的,函數,和區段。
•外部的: 在一個結構C中, 描述C稱述中的一種規格元素。見內部的。
•扇入: 描述有多個調用介面的提供指令或事件。
•扇出: 描述連接多個指令或事件實現的使用指令或事件。連接函數連接調用這些使用指令或事件的返回結果。
•介面: 當上下文清楚時,我們使用介面引用介面類型或介面實例。
•介面實例: 組件說明中,某一介面類型的實例。介面實例有實例名,角色(提供者或使用者),介面類型和可選的介面參數。沒有介面參數的介面實例是簡單的介面實例,帶有參數的是參數化介面實例。
•介面參數: 介面參數有介面參數名且一定是整數類型。參數化介面實例的每個參數清單都有(概念的)一個獨立的簡單的介面實例(並且,同樣的,在參數化指令或事件的情況下,都有獨立的簡單的指令或事件)。參數化介面實例允許運行時根據參數值在一套指令(或一套事件)中選擇運行。
•介面類型: 介面類型陳述兩組件,提供者和使用者間的交互作用。這種陳述使用一套指令或事件的形式。每個介面類型都有一個明確的名字。介面是雙向的: 介面供給者實現它的指令,介面使用者實現它的事件。
•中間函數: 表現組件指令和行為的虛函數,由整個程序的配線結構指定。見7.4節。
•內部的: 在一個結構 C 中, 描述C的組件列表中聲明的一個組件的規格元素。見外部的。
•模塊: 由C代碼提供實現的組件。
•命名空間: nesC 有標準的 C 變數(也為函數和宏使用),類型標識符(結構,聯合,枚舉標識名)和標籤命名空間。另外,nesC 有組件和介面類型命名空間用於組件和介面類型名。  
參數化指令,參數化事件,參數化介面實例,端點:見指令,事件,介面實例,端點。
提供,提供者:規格元素的一個角色。介面實例的提供者必須在介面中實現指令;提供指令和事件必須被實現。
K的提供指令:一個指令,它要麼是K提供的規格元素,要麼是K的提供介面的指令。
K的提供事件:一個事件,它要麼是K的規格元素,要麼是K使用介面的事件。
•範圍: nesC擁有標準的C全局,函數參數和段落範圍。另外,組件中還有說明和實現範圍和每一介面類型範圍。範圍被分為命名空間。
簡單指令,簡單事件,簡單介面實例:見指令,事件,介面實例。
•規格:說明組件和其他組件交互作用的規格元素列表。
•規格元素:規格中提供或使用的介面實例,指令或事件。
•作業:一個 TinyOS作業。
使用,使用者:規格元素的一種角色。介面實例的使用者必須實現介面中的事件。
K的使用指令:一個指令,它要麼是K的使用規格元素,要麼是K的使用介面的指令。
K的使用事件:一個事件,它要麼是K的使用規格元素,要麼是K的提供介面的事件。
•配線:由結構指定的組件規格元素間的連接。

----------------------------------------------
參考


[1] J. Hill, R. Szewczyk, A. Woo, S. Hollar, D. E. Culler, and K. S. J. Pister. 《感測器網路系統結構指南》. In Architectural Support for Programming Languages and
Operating Systems, pages 93–104, 2000. TinyOS is available at http://webs.cs.berkeley.edu.
[2] B. W. Kernighan and D. M. Ritchie. 《C語言程序設計》, Second Edition. Prentice
Hall, 1988.

上一篇[繽紛樂]    下一篇 [type]

相關評論

同義詞:暫無同義詞