nesc

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

基本介紹

  • 中文名:nesc
  • 功能:結構和內容的分離
  • 性質:擴展C的程式語言
  • 特點:接口有雙向性
基本信息,語言背景,使用環境,主要特性描述,語法介紹,辭彙解釋,安全守則,

基本信息

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

語言背景

nesc是一種擴展C的程式語言,主要用於感測器網路的編程開發,加州大學伯克利分校研發人員為這個平台開發出微型作業系統TinyOS和程式語言nesC,同時國內外很多大學和機構利用這一平台進行相關問題的研究。

使用環境

nesc主要用在Tinyos中,tinyos也是由nesc編寫完成的。TinyOS作業系統就是為用戶提供一個良好的用戶接口。基於以上分析,研發人員在無線感測器節點處理能力和存儲能力有限情況下設計一種新型的嵌入式系統TinyOS,具有更強的網路處理和資源收集能力,滿足無線感測器網路的要求。為滿足無線感測器網路的要求,研究人員在TinyOS中引入4種技術:輕執行緒、主動訊息、事件驅動和組件化編程。輕執行緒主要是針對節點並發操作可能比較頻繁,且執行緒比較短,傳統的進程/執行緒調度無法滿足(使用傳統調度算法會產生大量能量用在無效的進程互換過程中)的問題提出的。

主要特性描述

由於感測器網路的自身特點,面向其的開發語言也有其相應的特點。主動訊息是並行計算機中的概念。在傳送訊息的同時傳送處理這個訊息的相應處理函式ID和處理數據,接收方得到訊息後可立即進行處理,從而減少通信量。整個系統的運行是因為事件驅動而運行的,沒有事件發生時,微處理器進入睡眠狀態,從而可以達到節能的目的。組件就是對軟硬體進行功能抽象。整個系統是由組件構成的,通過組件提高軟體重用度和兼容性,程式設計師只關心組件的功能和自己的業務邏輯,而不必關心組件的具體實現,從而提高編程效率。

語法介紹

接口
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 事件.
組件說明
一個 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可能是參數化的或簡單的, 取決於其通信的規格元素的參數化或簡單狀態.
模組
模組用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的提供接口的事件。
·配線:由結構指定的組件規格元素間的連線。

安全守則

簡寫:NESC
中文名:美國國家電力安全守則
英文名:National Electric Safety Code(USA)

相關詞條

熱門詞條

聯絡我們