『TinyOS』 nesC 1.1 语言参考手册

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 节) 都发生被装载的代码中(例如., 没有对非自然的函数 " 看不见的 " 调用)。(see 注释5)

在装载文件预处理的时候,nesC 定义NESC 符号,用于识别nesC 语言和编译器版本的数字 XYZ。对于nesC 1.1, XYZ 至少为110 (see 注释6)。

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


9.1 装载 C文件X

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

-------------------------------------------
注释5 举例来说,现在的 nesC 编译器使用这些信息除去无法访问的代码.
注释6 NESC 符号不被在 nesC 的较早版本中定义.
--------------------------------------------


9.2 装载组件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节)。


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:
          includes BarTypes;                        typedef struct {
          interface Bar {                                int x;
                    command result_t bar(BarType arg1);           double 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-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_r-list )

---------------------------------------
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 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),所以连接器能找它。它还表明即使在程序任何地方都没有函数调用主函数,主函数同样能够被调用(自然的)。


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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值