nesC 语言参考手册

1 简介express

nesC 是对 C 的扩展 ,它基于体现 TinyOS 的结构化概念和执行模型而设计。 TinyOS 是为传感器网络节点而设计的一个事件驱动的操做系统,传感器网络节点拥有很是有限的资源 ( 举例来讲., 8K 字节的程序储存器,512个字节的随机存取储存器) 。TinyOS 用 nesC 从新编写。本手册描述 nesC 的 1.1 版本, 在第 3 段中概述了它与1.0版的不一样。数组

nesC 基本概念以下:安全

结构和内容的分离: 程序有组件构成, 它们装配在一块儿 (" 配线 ") 构成完整程序. 组件定义两类域, 一类用于它们的描述 ( 包含它们的接口请求名称) ,另外一类用于它们的补充。组件内部存在做业形式的协做。 控制线程能够经过它的接口进入一个组件。这些线程产生于一件做业或硬件中断。网络

根据接口的设置说明组件功能。 接口能够由组件提供或使用。被提供的接口表现它为使用者提供的功能,被使用的接口表现使用者完成它的做业所须要的功能。数据结构

         接口有双向性: 它们叙述一组接口供给者 (指令)提供的函数和一组被接口的使用者(事件)实现的函数。这容许一个单一的接口可以表现组件之间复杂的交互做用 (举例来讲.,当某一事件在一个回调以前发生时,对一些事件的兴趣登记)。 这是危险的,由于 TinyOS 中全部的长指令 (举例来讲. 发送包)是非中断的; 他们的完成由一个事件( 发送完成)标志。 经过叙述接口,一个组件不能调用发送指令除非它提供 sendDone 事件的实现。一般指令向下调用,好比, 从应用组件到那些比较靠近硬件的调用,而事件则向上调用。特定的原始事件与硬件中断是关联的 (这种关联是由系统决定的,所以在本参考手册中不做进一步描述)。并发

          组件经过接口彼此静态地相连。 这增长运行时效率,支持 rubust 设计, 并且容许更好的程序静态分析。异步

         nesC基于由编译器生成完整程序代码的需求设计。这考虑到较好的代码重用和分析。这方面的一例子是 nesC 的编译-时间数据竞争监视器。async

         nesC 的协做模型基于一旦开始直至完成做业 , 而且中断远源能够彼此打断做业. nesC 编译器标记由中断源引发的潜在的数据竞争。ide

 

本文是 nesC 的一本参考手册并不是个别指导。TinyOS tutoria1给出了对 nesC 的更亲切的介绍.函数

本文的其他部分构成以下: 第 2 节给出了本参考手册中使用的记号。. 第 3 节概述 nesC 1.1 的新特征。 第 4,5 节,6, 和 7介绍 nesC 接口和组件. 第 8 节给出 nesC的协做模型和数据竞争监视. 第 9 节解释 C文件, nesC 接口和组件是怎样被装配成一个应用. 第 10 节包含 nesC 的保留的各类特征. 最后,附录A彻底定义 nesC 的文法 (来自Kernighan and Ritchie (K&R) [2, pp234–239]附录A的对 C 文法述说的扩展),而附录 B 是本参考手册中所使用术语的专业词汇词典。

2 符号

打字机字体做为 nesC 代码和文件名,带任意下标的单个斜体字符用于表示 nesC 实体,举例来讲., " 组件 K" 或 " 数值 v"。nesC文法是ANSI C 文法的扩展. 咱们选择来自 Kernighan and Ritchie (K&R) 的附录A的 ANSI C 文法[2, pp234 –239]做为咱们介绍的基础。在这里咱们将不重复ANSI C文法。. 斜体字是非终端机和非文字的终端机,打字机字体和符号是文字的终端机。 下标 opt 表示可选择的终端机或非终端机。在一些情形中,咱们改变一些 ANSI C 文法规则. 咱们用下面的方式表示:aso 为现存的非终端机指出新增的内容,repaced by表示替换一现有的非终点的.nesC 的结构解释给出对应的文法片断。在这些片断中,咱们有时使用 . . . 表现省略( 与当前不相关的解释). 附录A给出完整的 nesC 文法.一些例子使用来自 C99 标准 inttypes.h 文件的 uint8 t 和 uint16 t 类型.

3 变化

nesC 1.0版本同1.1版本的变化以下:

1. 原子的陈述. 这些单一化协同数据结构的实现,可以被新的编译- 时间数据竞争监视器识别。

2. 编译- 时间数据竞争监视为可能的协同的二个中断操做者 , 或一个中断操做者和一件做业同时存取变量提出警告

3. 指令和事件必须明确地标出存储类型说明才能安全地被中断操做者执行。

4. 对指令或"扇出"事件的调用返回结果自动地被新的类型- 特性的组合器执行联合。

5. uniqueCount 是一个新的 " 常数功能 ", 具备独特的做用.

6. NESC 预处理程序符号指出语言版本. 对于 nesC 1.1版本它是 110 。

4 接口

nesC 的接口有双向性: 它们描述一个多功能的两组件(供给者和使用者)之间的交互渠道.。 接口叙述一组叫作指令的, 被接口的供给者实现的,被命名的功能和一组叫作事件的, 被接口的使用者实现.的,被命名的功能。

本节解释接口如何被指定, 第 5 节解释组件如何描述它们提供和使用的接口, 第 6 节解释在 C代码中指令和事件如何被调用和实现,而第 7 节解释组件接口如何被一块儿联编.

接口被接口类型指定,以下:

 

nesC-fie:

incudes-istopt interface

. . .

interface:

interface identifier { decaration-ist }

storage-cass-specifier: aso one of

command event async

 这声明接口类型标识符. 这一标识符有全局的做用范围而且属于分开的命名空间,组件和接口类型命名空间。 如此全部接口类型都有清楚的名字以区别于其它接口和全部组件, 同时能不和通常的 C的声明发生任何冲突。

声明列表中,每一个接口类型都有一个分开的声明范围。声明列表必须由有指令或事件存储类型的功能描述组成( 不然, 会发生编译-时间错误). 可选的 async 关键字指出指令或事件能在一个中断处理者中被运行。

经过包含列表,一个接口能可选择地包括 C 文件 (见第9节)。

一个简单的接口以下:  

interface SendMsg {

command resut_t send(uint16_t address, uint8_t ength, TOS_MsgPtr msg);

event resut_t sendDone(TOS_MsgPtr msg, resut_t success);

}

 SendMsg 接口类型提供者必须实现发送指令, 而使用者必须实现 sendDone 事件.

5 组件说明

一个 nesC 组件或是一个模块 (第 6 节) 或一个结构 (第 7 节):

nesC-fie:

incudes-istopt modue

incudes-istopt configuration

. . .

modue:

modue identifier specification modue-impementation

configuration:

configuration identifier specification configuration-impementation

组件的名字由标识符指定. 这一标识符有全局的做用范围而且属于组件和接口类型命名空间. 一个组件介入两个分组件的做用域::一个规格做用域,属于 C 中全局的做用域,和一个实现做用域属于规格做用域。

经过包含列表,一个组件能可选择地包括 C 文件 (见第9节).

组件规格列出该组件提供或使用的规格元素 (接口请求,指令或事件)。 就如咱们在第 4 节中见到的,一个组件必须实现它提供接口的指令和它的使用的接口事件。另外,它必须实现它提供的指令和事件。

典型地,指令向下调用硬件组件,而事件向上调用应用组件 (这表现为nesC应用如一个应用组件处于顶端的组件曲线图)。 一个控制线程只有经过它的规格元素越过组件。

每种规格元素有一个名字 (接口实例名,命令名或事件名).这些名字属于总组件-规格做用域的变量命名空间。

specification:

{ uses-provides-ist }

uses-provides-ist:

uses-provides

uses-provides-ist uses-provides

uses-provides:

uses specification-eement-ist

provides specification-eement-ist

specification-eement-ist:

specification-eement

{ specification-eements }

specification-eements:

specification-eement

specification-eements specification-eement

一个组件说明中能够有多个使用和提供指令。多个使用和提供规格元素能够经过包含在{ and}中而组合在一个指令中。举例来讲,下面两个说明是同样的:

modue A1 {                            modue A1 {

uses interface X;                     uses {

uses interface Y;                             interface X;

} ...                                                        interface Y;

}

} ...

一个接口实例描述以下:

specification-eement:

interface renamed-identifier parametersopt

. . .

renamed-identifier:

identifier

identifier as identifier

interface-parameters:

[ parameter-type-ist ]

 

接口实例声明的完整语法是 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-eement:

decaration

. . .

storage-cass-specifier: aso one of

command event async

若是该声明不是带有指令或事件存储类型的函数声明就会产生一编译- 时间错误。在接口中, 文法指出指令或事件能被一中断操纵者运行。

做为接口实例, 若是没有指定接口叁数,指令 (事件)就是简单的指令 (简单的事件),若是接口叁数是指定的,就是参数化指令 (参数事件)。接口叁数被放置在通常的函数叁数列表以前,举例来讲.,

command void send[uint8 t id](int x):

direct-decarator: aso

direct-decarator interface-parameters ( parameter-type-ist )

. . .

注意接口叁数只在组件说明里面指令或事件上被容许, 而不容许在接口类型里面.

这儿有一个完整的规格例子:

configuration GenericComm {

provides {

interface StdContro as Contro;

// 该接口以当前消息序号做参数

interface SendMsg[uint8_t id];

interface ReceiveMsg[uint8_t id];

}

uses {

//发送完成以后为组件做标记

//重试失败的发送

event resut_t sendDone();

}

} ...

在这个例子中,通常:

          提供简单的接口实例类型 StdContro 的控制.

          提供接口类型 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多是参数化的或简单的, 取决于其通讯的规格元素的参数化或简单状态.

6 模块

模块用C代码实现组件说明:

modue-impementation:

impementation { transation-unit }

 

这里编译基本单位是一连串的 C 声明和定义 ( 见K& R[2 , pp234 –239])。

模块编译基本单位的顶层声明属于模块的组件说明域。这些声明的范围是模糊的并且能够是: 任意的标准 C声明或定义,一种做业声明或定义,指令或事件实现.

6.1 实现模块的说明

编译基本单位必须实现模块的全部的提供指令 (事件)a (例如., 全部的直接提供指令和事件, 以及提供接口的全部指令和使用接口的全部事件). 一个模块能调用它的任一指令和它的任一事件的信号.

这些指令和事件的实现由以下的 C 语法扩展指定:

storage-cass-specifier: aso one of

command event async

decaration-specifiers: aso

defaut decaration-specifiers

direct-decarator: aso

identifier . identifier

direct-decarator interface-parameters ( parameter-type-ist )

 

简单指令或事件a由带有存储类型指令或事件的C 函数定义的语法实现 (注意容许在函数名中直接定义的扩展)。另外,语法关键字必须被包含若是它被包含在a的声明中。举例来讲,在SendMsg类型的提供接口Send的模块中:

command resut_t Send.send(uint16_t address, uint8_t ength, TOS_MsgPtr msg) {

...

return SUCCESS;

}

带有接口参数P的参数指令或事件a,由带有存储类型指令或事件的函数定义的C文法实现,这时,函数的普通参数列表要以P做为前缀,并带上方括号 ( 这与组件说明中声明参数化指令或事件是相同的文法)。这些接口叁数声明P 属于a的函数叁数做用域并且和普通的函数参数有相同的做用域。举例来讲,在SendMsg类型提供接口Send[uint8 tid]的模块中:

command resut_t Send.send[uint8_t id](uint16_t address, uint8_t ength,

TOS_MsgPtr msg) {

...

return SUCCESS;

}

如下状况将报告编译- 时间错误:

         提供指令或事件没有实现。

         类型标志,可选择的接口叁数和指令或事件语法关键字的存在或缺失,或与模块说明不匹配

6.2 调用命令和事件信号

对 C 语法的下列扩展用于调用事件和向指令发出信号:

postfix-expression:

postfix-expression [ argument-expression-ist ]

ca-kindopt primary ( argument-expression-istopt )

. . .

ca-kind: one of

ca signa post

 

一个简单的指令a使用ca _(...)调用, 一件简单的事件使用signa a(...)发送讯号。举例来讲,在一个模块中使用SendMsg类型接口Send:ca Send.send(1,sizeof(Message), &msg1)。

一个参数指令a(个别地,一件事件)有 n个接口叁数,类型为t1 , . . . , t n由接口参数表达式e1 , . . . ,en调用以下:ca _[e1, . . . , en](...) (个别地,signa _[e1, . . . , en](...))。接口叁数表达式 ei 必须分配类型t i; 实际的接口叁数值是ei影射到t i. 举例来讲, 在一个组件中使用类型 SendMsg 的接口Send[uint8 t id]:

int x = ...;

ca Send.send[x + 1](1, sizeof(Message), &msg1);

 

指令和事件的执行是当即的,也就是,调用和发送信号行为和函数调用是一样地。实际的指令或事件是由调用仍是信号表达运行取决于程序结构联系说明。 这些联系说明可能指定0,1 或更多的实现将被运行。当超过 1个实现被运行, 咱们说模块的指令或事件为"扇出"。

一个模块能为一使用指令或事件a指定默认的调用或信号实现。提供指令或事件的默认实现会引发编译-时间错误。若是a未与任何指令或事件实现联系,默认的实现将被执行。默认的指令或事件由带有默认关键字的指令或事件实现前缀定义:

decaration-specifiers: aso

defaut decaration-specifiers

 

举例来讲, 在一个类型 SendMsg使用接口Send的模块中:

defaut command resut_t Send.send(uint16_t address, uint8_t ength,

TOS_MsgPtr msg) {

return SUCCESS;

}

/* 容许调用即便接口发送未链接*/

... ca Send.send(1, sizeof(Message), &msg1) ...

 

第 7.4 节叙述实际上什么指令或事件实现被运行以及调用和信号表达返回什么结果.

6.3 做业

做业是一个独立的控制点,由一个返回空存储类型的无二义性地函数定义:task void myTask() { ... }。做业也能预先声明,举例来讲., task void myTask();

做业经过前缀post调用通知,举例来讲., post myTask()。通知返回迅速;若是独立执行通知成功则返回1,不然返回0。通知表达式的类型是unsigned char。

storage-cass-specifier: aso one of

task

ca-kind: aso one of

post

 

nesC的协做模型,包括做业,在第8节中详细陈述。

6.4 原子的陈述

原子的陈述:

atomic-stmt:

atomic statement

确保陈述被运行 " 好像 " 没有其它的运算同时发生。它用于更新并发的数据结构的互斥变量,等等。 一简单的例子是:

boo busy; //全局

void f() {

boo avaiabe;

atomic {

avaiabe = !busy;

busy = TRUE;

}

if (avaiabe) do_something;

atomic busy = FASE;

}

 

原子的区段应该很短, 虽然这经常并非必须的。控制只能" 正常地 " 流入或流出原子的陈述: 任何的 goto, break或continue,跳转入或出一原子陈述都是错误的。返回陈述决不容许进入原子陈述。

第 8 节讨论原子和 nesC协做模型和数据竞争监视器之间的关系。

7 结构

结构经过链接,或配线,集合其余组件实现一个组件说明:

configuration-impementation:

impementation { component-istopt connection-ist }

 

组件列表列出用来创建这一个结构的组件,链接列表指明各组件之间,以及与结构说明之间是怎样装配在一块儿的。在这一节的其他部分中,咱们调用来自结构的外部的规格元素

, 和来自结构的内在的成份之一的规格元素。

7.1 包含组件

组件列表列出用来创建这一个结构的组件。在结构里面这些组件可随意的重命名,使用共同外形规格元素,或简单的改变组件结构从而避免名称冲突。(以免必须改变配线)为组件选择的名字属于成份的实现域。

component-ist:

components

component-ist components

components:

components component-ine ;

component-ine:

renamed-identifier

component-ine , renamed-identifier

renamed-identifier:

identifier

identifier as identifier

 

若是二个组件使用as给出相同的名字,则会发生编译时间错误(举例来讲., components

X, Y as X)。

只有一个个别的例子:若是组件 K 被用于二不一样的结构 ( 或甚至两次用于相同的结构里面), 在程序中仍然只有 K(及它的变量) 的惟一实例。

7.2 配线

配线用于链接规格元素 (接口,指令,事件)。本节和下一节(第 7.3 节) 定义配线的语法和编译-时间规则。第 7.4 节详细说明程序配线声明是如何指出在每一个调用和信号表达中哪一个函数被调用。

connection-ist:

connection

connection-ist connection

connection:

endpoint = endpoint

endpoint -> endpoint

endpoint <- endpoint

endpoint:

identifier-path

identifier-path [ argument-expression-ist ]

identifier-path:

identifier

identifier-path . identifier

配线陈述链接二个端点。每一个端点的标识符路径指明一个规格要素。自变量表达式列表可选的指出接口参数值。若是端点的规格要素是参数化的,而端点又没有参数值,那么咱们说该端点是参数化的。若是一个端点有参数值,而下面的任一事件成立时,就会产生一个编译时间错误:

         叁数值不全是常量表达式.

         端点的规格元素不是参数化的.

         参数个数比规格要素中限定的叁数个数多 ( 或少)

         叁数值不在规格元素限定的叁数类型范围中。

若是端点的标识符路径不是如下三种形式之一,就会产生一个编译时间错误:

         X, 此处X命名一种外部的规格元素.

         K.X,此处K 是组件列表中的一个组件,而X是K 的规格元素。

K ,此处K是组件列表中的一些组件名。这种形式用在固定的链接中,将在第 7.3 节中讨论。 注意,当叁数值指定时这种形式不可以使用。

nesC 有三种配线陈述:

          endpoint1=endpoint2:( 赋值配线) 任何链接包括一外部规格元素。这些有效地使两规格元素相等。设S1是endpoint1的规格要素,S2是endpoint2的规格要素。下面两个条件之一必须知足,不然就会产生编译时间错误:

– S1 是内部的, S2 是外部的 (反之亦然) ,而且 S1 和 S2都是被提供或都是被使用

– S1 和 S2 都是外部的,并且一个被提供,而另外一个被使用.

endpoint1->endpoint2:( 联编配线) 一个链接包括二种内在的规格元素。.联编配线老是连结一由endpoint1指定的使用规格元素到一endpoint2指定的提供规格元素。若是这两个条件不能知足, 就会发生编译-时间错误.。

         endpoint1<- endpoint2 与endpoint2 -> endpoint1是等价的。

在配线的全部三种类型中,两被指定的规格元素必须是一致的,就是说., 它们必须都是指令,或都是事件, 或都是接口实例. 同时, 若是它们是指令(或事件),则它们必须有相同的函数名 若是他们是接口实例,它们必须有相同的接口类型。他们必定是有相同的接口类型的. 若是这些条件不能知足, 就会发生编译-时间错误.。

若是一个端点是参数化的,则另外一个必须也是并且必须有相同的叁数类型;不然就会发生编译-时间错误.。

相同的规格元素能够被屡次链接,举例来讲.,:

configuration C {

provides interface X;

} impementation {

components C1, C2;

X = C1.X;

X = C2.X;

}

 

在这个例子中,当接口X中的命令被调用时,屡次的配线将会致使接口X的事件的多重信号 ("扇入"),以及多个函数的执行("扇-出")。注意,当二个结构独立地联结相同接口的时候,多重配线也能发生,举例来讲.:

configuration C { }                configuration D { }

impementation {                  impementation {

components C1, C2;                components C3, C2;

C1.Y -> C2.Y;                    C3.Y -> C2.Y;

}                                }

 

全部的外部规格元素必须配线,不然发生编译-时间错误. 但是,内部的规格元素能够不链接 (它们可能在另一个结构中配线,或者若是模块有适当的默认事件或指令实现,他们能够不配线).

7.3 隐含链接

隐含链接能够写成K1 <- K2.X 或K1.X <- K2 (=和->是等价的). 该用法经过规格元素K1 (不妨K2)来引用规格元素 Y,所以K1.Y <- K2.X (不妨 K1.X <- K2.Y )造成一个合法联结。若是能正确的引用Y,则链接创建,不然发生编译-时间错误。举例来讲:

modue M1 {                          modue M2 {

provides interface StdContro;             uses interface StdContro as SC;

} ...                                  } ...

configuration C { }

impementation {

interface X {                           modue M {

command int f();                        provides interface X as P;

event void g(int x);                      uses interface X as U;

}                                         provides command void h();

} impementation { ... }

configuration C {

provides interface X;

provides command void h2();

}

impementation {

components M;

X = M.P;

M.U -> M.P;

h2 = M.h;

}

图 1: 简单的配线例子

components M1, M2;

M2.SC -> M1;

}

 

M2.SC -> M1 这一行与M2.SC -> M1.StdContro. 是等价的。

7.4 配线语义

咱们首先撇开参数化接口讨论配线语义. 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的结果类型),列表经过链接调用中间函数返回结果构成。返回空值的中间函数适合不相

链接的指令或事件;返回两个或以上值的中间函数适合“扇出”。

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

nesC容许在没有直接中间函数的状况下编译,因此本节中描述的行为没有运行开销,实际的函数调用须要参数化的指令或事件。

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

中间函数和结构 一个结构的配线说明指定中间函数体。咱们首先扩展配线说明到中间函数而不限于规格元素,并取消配线说明中= 和->的区别。咱们用 I1<-> I2 表示中间函数I1 和I2之间的连结。举例来讲,图 1中的结构C 叙述了下列中间函数链接:

IC.X.f<-> IM.P.f     IM.U.f<-> IM.P.f     IC.h2<->IM.h

IC.X.g<-> IM.P.g     IM.U.g<->IM.P.g

在结构 C 的链接I1 <-> I2中,二个中间函数之一是被调用的,另外一个是调用者。若是下列任一条件成立(咱们使用内部或外部的用辞做规格说明并不妨碍结构 C包含链接),则I1(一样地,I2)是被调用的:

         若是 I1 符合一件被提供指令或事件的内部规格元素.

         若是 I1 符合一件被使用指令或事件的外部规格元素.

         若是 I1 符合一个接口实例X 的指令,而X是内部的且被提供或外部的且被使用的规格元素.

         若是 I1 符合一个接口实例X 的事件,而X是外部的且被提供或内部的且被使用的规格元素.

若是这些状况没有一个成立,则I1 调用者。7.2 节的配线规则确保一个链接 I1<->I2 不会同时链接二个调用者或二个被调用者。图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。其结果是

一个单独的调用返回列表。表达式ca a(e1, . . . , en)性质以下:

         自变量e1, . . . , en 被赋值为v1, . . . , vn.。

         a对应的中间函数被以自变量v1, . . . , vn调用,返回结果列表.

         若是 =(w)( 一个独立列表),调用的返回结果就是 w.

若是 =(w1,w2, . . . ,wm) (二或更多的元素),调用的结果仰赖于a的返回类型t。若是t=void,则结果是void。不然,t 必定有一联合函数c( 第 10.3节演示联合函数是如何联合类型的),不然发生编译-时间错误。联合函数接受类型t 的两个值而且返回一个类型t的结果。该调用的结果是c(w1, c(w2, . . . , c(wm−1,wm))) ( 注意中元素次序是任意的).

 

ist of int IM.P.f() {              ist of void IM.P.g(int x) {  

return ist(M.P.f());              ist of int r1 = IC.X.g(x);

}                               ist of int r1 = IM.U.g(x);

return ist concat(r1, r2);

}

ist of int IM.U.f() {             ist of void IM.U.g(int x) {

 return IM.P.f();                  return ist(M.U.g(x));

}                           }

ist of int IC.X.f() {              ist of void IC.X.g(int x) {

 return IM.P.f();                    return empty ist;

}                            }

ist of void IC.h2() {              ist of void IM.h() {

return IM.h();                    return ist(M.h());

}                            }

                                                                      

图 2: 图1的中间函数

 

         若是  为空则默认以v1, . . . , vn,为自变量调用执行a,并返回该调用结果。第 7.4.2 节代表若是为空且a没有默认实现则会发生一编译- 时间错误。

信号表达式的规则是同样的。

 

中间函数举例  图 2使用类C的语法演示了图 1中组件产生的中间函数,其中ist(x)产生一个包含X的独立列表,空列表是表示含0个元素的列表的常量,链接列表如锁链般链接两个列表。对M.P.f, M.U.g, M.h的调用,调用模块M中实现的指令和事件(未给出)。

7.4.1 配线和参数化函数

若是组件K的一条指令或事件a带有类型t1, . . . ,tn的接口叁数,则对每一数组(v1 : t1, . . . , vn :

tn)存在一个中间函数Ia,v1,...,vn 。

在模块中,若是中间函数Iv1,...,vn符合参数化的提供指令(或事件)a,则Iv1,...,vn中对a的实现的调用将传递v1, . . . , vn做为a的接口参数。

下面是对表达式ca _[e01, . . . , e0m](e1, . . . , en)讨论:

         自变量e1, . . . , en被赋值为 v1, . . . , vn。

         自变量e01, . . . , e0m被赋值为v01 , . . . , v0m。

         v0i 对应ti 类型, 这里t i 是a的第i个接口叁数的类型。

         a对应的中间函数Iv01 ,...,v0m被以参数v1, . . . , vn,调用,返回列表。

         若是  有一个或更多的元素, 在非参数化的情形下产生调用结果

         若是  为空,a的默认实现会被以自变量v1, . . . , vn,,以接口参数值v01 , . . . , v0m调用,且返回该调用的结果。7.4.2节代表若是为空且a没有默认实现,则会产生编译-时间错误。

信号表达式的规则是同样的.

配线说明中的一个端点关系到一参数化规格元素时,有二种情形:

         端点指定叁数值 v1, . . . , vn。若端点符合指令或事件 a1, . . . ,am ,则相应的中间函数为Ia1,v1,...,vn,. . . , Iam,v1,...,vn且配线方式不变。

         端点未指定叁数值. 在这状况下,配线说明的两个端点都对应相同接口参数类型t1, . . . ,tn.的参数化规格元素。 若是一个端点对应指令或事件 a1, . . . , am 而另外一端点对应指令或事件 β1, . . . , βm,则对全部的1<=i<= m和全部的数组(w1 : t1, . . . ,wn : tn)有链接Iai,w1,...,wn<-> Iβi,w1,...,wn (就是说., 端点是为全部对应的叁数值链接的).。

7.4.2 应用级的需求

一个应用的配线说明必须知足两个需求, 不然就会发生编译时间错误:

         没有只包含中间函数的无限循环.

         在应用模块中的每一个ca a ( 或signa a)表达式中:

–若是调用是非参数化的:若是调用返回空的结果列表,则a必定有默认实现 (结果列表中元素个数只仰赖于配线)。

–若是调用是参数化的:若是a的接口叁数的任何替代值都返回空结果列表,则a一定有默认的实现 (给定参数值数组的返回结果列表中元素数目只仰赖于配线)。

注意这种状况不考虑用来在调用点叙述接口参数值的表达。

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

该调用的特点是包含在几个指令执行间的运行时选择——这是中间函数惟一的一处运行时开销

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

8 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 会报告编译- 时间错误。

这确保那些不安全的代码不会在中断时无心中被调用。

 

 

 

9 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 1.1, XYZ 至少为110。

 

装载C 文件,nesC组件及接口类型的过程包括定位对应的资源文件。文件定位的机制不是本参考手册中所要讨论的。要详细了解通用编译器是如何做业的,请阅读《the ncc man page.》

 

9.1 装载 C文件X

 

若是 X 已经被装载,就不用再作什么。不然, 就要定位并预处理文件 X.h。C宏定义 ( 由 # define和 #undef) 的改变会影响到全部的后面的文件预处理。来自被预处理的文件X.h的 C声明和定义会进入C全局命名环境,所以对全部的后来的 C文件加工,接口类型和组件是有影响的。

 

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

 

5举例来讲,如今的 nesC 编译器使用这些信息除去没法访问的代码.

 

6NESC 符号不被在 nesC 的较早版本中定义.

 

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

 

9.2 装载组件K

 

若是K已经被装载,就不用再作什么。不然, 就要定位并预处理文件 X.nc。对C宏定义( 由 # define和 #undef)的变化被忽略。使用下面的语法分析预处理文件:

 

nesC-fie:

 

incudes-istopt interface

 

incudes-istopt modue

 

incudes-istopt configuration

 

incudes-ist:

 

incudes

 

incudes-ist incudes

 

incudes:

 

incudes identifier-ist ;

 

 

 

若是 X.nc没有定义模块K 或结构 K,将报告编译-时间错误。不然,全部的包含列表指定的C文件都将被装载 (9.1节)。而后,组件说明中用到的全部接口类型都将被装载(9.3节)。接着,处理组件说明(第5节). 若是 K 是一个结构, K 指定的 (第 7.1 节) 的全部组件被

 

装载 (9.2节)。最后,K的实现被处理 (第6节和第7节)。

 

9.3 载入接口类型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:

 

incudes BarTypes;                        typedef struct {

 

interface Bar {                                int x;

 

command resut_t bar(BarType arg1);           doube y;

 

}                                      } BarType;

 

接口Bar的定义能参考Bar类型, 一样任何使用和提供接口Bar组件也能(装载任何这些组件说明或实现以前,都要先装载接口Bar,天然还有BarTypes.h)

 

10 多样性

 

10.1没有自变量的函数的C声明的旧风格

 

没有自变量的 nesC函数使用()声明, 而不是 (void)。后者的用法将报告编译-时间错误.。

 

旧式的C声明(用())和函数定义(在自变量以后指定参数列表)在接口和组件中是不容许的(会引发编译-时间错误)。

 

注意这些变化都不用于C文件(以便现有的.h 文件能被不变的使用).

 

10.2 // 注释

 

nesC 容许C,接口类型和组件文件中的 //注释 。

 

10.3 属性

 

nesC使用gcc的属性语法声明函数的一些属性,变量及类型。这些属性能够放置在声明(在声明符以后) 或函数定义.(在叁数列表以后)上。 x 的属性是所有x 的声明和定义.上的全部属性的集合。

 

nesC 的属性语法是:

 

init-decarator-ist: aso

 

init-decarator attributes

 

init-decarator-ist , init-decarator attributes

 

function-definition: aso

 

decaration-specifiersopt decarator attributesdecaration-istoptcompound-statement

 

attributes:

 

attribute

 

attributes attribute

 

attribute:

 

attribute ( ( attribute-ist ) )

 

attribute-ist:

 

singe-attribute

 

attribute-ist , singe-attribute

 

singe-attribute:

 

identifier

 

identifier ( argument-expression-ist )

 

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

 

gcc不容许函数定义中参数列表后面的属性.

 

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

 

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 resut_t __attribute__((combine(rcombine)));

 

resut_t rcombine(resut_t r1, resut_t r2)

 

{

 

return r1 == FAI ? FAI : r2;

 

}

 

当联合指令(或事件)返回类型是t 时,叙述逻辑-类似的行为。详细的语义见第 7.4 节。

 

若是类型 t 的联合函数c没有类型t c(t,t),就会发生编译时间错误。

 

使用属性的例子:在文件 ReaMain.td 中:

 

modue ReaMain { ... }

 

impementation {

 

int main(int argc, char **argv) __attribute__((C, spontaneous)) {

 

...

 

}

 

}

 

这个例子代表主函数实际上应该出如今 C全局命名空间 (C),因此链接器能找它。它还代表即便在程序任何地方都没有函数调用主函数,主函数一样可以被调用(天然的)。

 

10.4 编译-时间常量函数

 

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来分配正确个数的定时器数据结构。

 

附录语法

 

读本语法时,请翻阅Kernighan and Ritchie (K&R) [2, pp234–239]的附录A。

 

下列关键字对 nesC 是新的: as, ca, command, components, configuration, event,

 

impementation, interface, modue, post, provides, signa, task, uses, incudes. 这些关键字在 C 文件中是不被保留的。 对应的 C 符号,经过加上_nesc_keyword前缀(举例来讲.,_ nesc _keyword _as).,在nesC文件中是可用的。

 

nesC全部的标识符均以_nesc开头保留做为内部使用。TinyOS 保留全部的标识符以TOS_和TOSH_开头。

 

nesC文件遵循nesC文件要求; .h 文件经过incudes包含,遵循来自 K&R 的指令编译单位

 

新的规则:

 

nesC-fie:

 

incudes-istopt interface

 

incudes-istopt modue

 

incudes-istopt configuration

 

incudes-ist:

 

incudes

 

incudes-ist incudes

 

incudes:

 

incudes identifier-ist ;

 

interface:

 

interface identifier { decaration-ist }

 

modue:

 

modue identifier specification modue-impementation

 

modue-impementation:

 

impementation { transation-unit }

 

configuration:

 

configuration identifier specification configuration-impementation

 

configuration-impementation:

 

impementation { component-istopt connection-ist }

 

component-ist:

 

components

 

component-ist components

 

components:

 

components component-ine ;

 

component-ine:

 

renamed-identifier

 

component-ine , renamed-identifier

 

renamed-identifier:

 

identifier

 

identifier as identifier

 

connection-ist:

 

connection

 

connection-ist connection

 

connection:

 

endpoint = endpoint

 

endpoint -> endpoint

 

endpoint <- endpoint

 

endpoint:

 

identifier-path

 

identifier-path [ argument-expression-ist ]

 

identifier-path:

 

identifier

 

identifier-path . identifier

 

specification:

 

{ uses-provides-ist }

 

uses-provides-ist:

 

uses-provides

 

uses-provides-ist uses-provides

 

uses-provides:

 

uses specification-eement-ist

 

provides specification-eement-ist

 

specification-eement-ist:

 

specification-eement

 

{ specification-eements }

 

specification-eements:

 

specification-eement

 

specification-eements specification-eement

 

specification-eement:

 

decaration

 

Interface renamed-identifier parametersopt

 

parameters:

 

[ parameter-type-ist ]

 

 

 

改变的规则:

 

storage-cass-specifier: aso one of

 

command event async task norace

 

decaration-specifiers: aso

 

defaut decaration-specifiers

 

direct-decarator: aso

 

identifier . identifier

 

direct-decarator parameters ( parameter-type-ist )

 

init-decarator-ist: aso

 

init-decarator attributes

 

init-decarator-ist , init-decarator attributes

 

function-definition: aso

 

decaration-specifiersopt decarator attributes decaration-istopt compound-statement

 

attributes:

 

attribute

 

attributes attribute

 

attribute:

 

_attribute_ ( ( attribute-ist ) )

 

attribute-ist:

 

singe-attribute

 

attribute-ist , singe-attribute

 

singe-attribute:

 

identifier

 

identifier ( argument-expression-ist )

 

statement: aso

 

atomic-statement

 

atomic-statement:

 

atomic statement

 

postfix-expression: repaced by

 

primary-expression

 

postfix-expression [ argument-expression-ist ]

 

ca-kindopt primary ( argument-expression-istopt )

 

postfix-expression . identifier

 

postfix-expression -> identifier

 

postfix-expression ++

 

postfix-expression --

 

ca-kind: one of

 

ca signa 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的提供接口的事件。

•配线:由结构指定的组件规格元素间的链接。

相关文章
相关标签/搜索