2009年5月7日 星期四

The component's frame in TinyOS

TinyOS里 Component有一个Frame的概念,挺起来确实有点悬乎乎的,不知道是啥子东西。

孙利民的《无线传感器网络》第343和344页有下面三个说法:

1.一个经过封装的私有数据帧(data frame);

2.任务、命令和事件处理程序在帧的上下文中这行并切换帧的状态;

3.TinyOS采用静态分配存储帧,这样在编译时就可以决定全部应用程序所需要的存储器空间。帧是一种特殊的符合C语法的结构体(struct),它不仅采用静态分配而且只能由其所属的组件直接访问。TinyOS不提供动态的存储保护,组件之间的变量越权访问检查是在编译过程中完成的。除了允许计算存储器空间要求的最大值,帧的预分配可以防止与动态分配相关的额外开销,并且可以避免与指针相关的错误。另外,预分配还可以节省执行事件的开销,因为变量的位置在编译时就确定了,而不用通过指针动态地访问其状态;

后来去官方的帮助,找到主题“[Tinyos-help] About Components Frame”,Phil做了还算详细的解答。里面提到:
1."nesC compiles to C; take a look at build//app.c to see the generated C. The C compiler/assembler then assigns the layout of variables in the data or bss sections, which are distinct from the stack."
2."the FRAME concept just means the variables and data(initialized or not, as in data or bbs sections of the exec elf files) ofthe C program”

关于the data or bss sections:
bss段(未手动初始化的数据)并不给该段的数据分配空间,只是记录数据所需空间的大小。
data段(已手动初始化的数据)段则为数据分配空间,数据保存在目标文件中。
需要更详细了解the data or bss sections的可以参考一下赵迥老师的《Linux内核完全剖析》第3章节。或者《Linkers and Loaders.pdf》,它有中文版。

现在最最需要我们做的是什么呢,就是去查看build//app.c。这个app.c文件是nesC编译器编译之后生成的C文件,比如你make micaz之后就会生成。我以前装的是TinyOS1.X的,同样我现在也拿其中一个应用程序“CntToLedsAndRfm”来说,其路径为:
“opt/tinyos-1.x/apps/CntToLedsAndRfm/build/micaz/app.c”

通过命令make micaz docs我们可以在“opt/tinyos-1.x/doc/nesdoc/micaz/index.html”中点击“CntToLedsAndRfm”的链接,然后我们看Component Graph,了解到最后CntToLedsAndRfm会wire“AMStandard.nc”这个组件。这个组件source: tos.system.AMStandard.nc。

我们截取AMStandard.nc的实现(implementation)中的一部分代码:
implementation{
bool state;
TOS_MsgPtr buffer;
uint16_t lastCount;
uint16_t counter;

// Initialization of this component
command bool Control.init() {
result_t ok1, ok2;
call TimerControl.init();
ok1 = call UARTControl.init();
ok2 = call RadioControl.init();
state = FALSE;
lastCount = 0;
counter = 0;
dbg(DBG_BOOT, "AM Module initialized\n");
return rcombine(ok1, ok2);
}
.............................

然后我们对照CntToLedsAndRfm/build/micaz/app.c中的相应代码:
# 82 "C:/tinyos/cygwin/opt/tinyos-1.x/tos/system/AMStandard.nc"
bool AMStandard$state;
TOS_MsgPtr AMStandard$buffer;
uint16_t AMStandard$lastCount;
uint16_t AMStandard$counter;

static inline
# 88 "C:/tinyos/cygwin/opt/tinyos-1.x/tos/system/AMStandard.nc"
bool AMStandard$Control$init(void)
{
result_t ok1;
result_t ok2;
AMStandard$TimerControl$init();
ok1 = AMStandard$UARTControl$init();
ok2 = AMStandard$RadioControl$init();
AMStandard$state = FALSE;
AMStandard$lastCount = 0;
AMStandard$counter = 0;
{ } ;
return rcombine(ok1, ok2);
}

首先:
bool state;
TOS_MsgPtr buffer;
uint16_t lastCount;
uint16_t counter;
这一个就是所谓的帧,它们在生成的app.c后都变成了全局变量,对哦,就是静态分配的,不管这些所谓的帧里的变量有没有初始化。其实初始化的话就放到data段中,没被初始化的就放在bss段中而已,根本不影响它这个“帧”的地位。
而上面说的“帧是一种特殊的符合C语法的结构体(struct)”怎么理解呢?
我以前看过一份“System Architecture Directions for Networked Sensors.ppt---Qiuhua Cao(qc9b@cs) Computer Science Department University of Virginia”。可以通过网络上搜索获得。这应该是早期的nesC语法或者早期的TinyOS编程模式。PPT里面提及:
Concepts in TinyOS (cont.)
Frame
ØContains all permanent state for component (lives across events, commands, and threads)
ØThreads, events, and commands execute inside the component’s frame
ØOnly one per component
•Like static class variables, not internal class variables.(类似C++总的静态数据成员,它被类的所有对象共享,而不是属于某个对象的,在存储上只需要存储一处,就可以供所有对象使用,只要对静态数据成员的值更新一次,所有对象的该静态成员值都被更新)
Ø Fixed size
Ø Statically allocated at compile time

Frame example:
Ø Frame declaration
#define TOS_FRAME_TYPE AM_obj_frame
TOS_FRAME_BEGIN(AM_obj_frame) {
int addr;
char type;
char state;
char* data;
char msgbuf[30];
}
TOS_FRAME_END(AM_obj_frame);

ØUse of frame Variables:
VAR(state) = 0;

这样开来,TOS_FRAME_BEGIN(AM_obj_frame) { ××××××} TOS_FRAME_END(AM_obj_frame); 就应该是所谓的符合C语法的结构体(struct),呵呵。

它不仅采用静态分配而且只能由其所属的组件直接访问,这个我们又怎么理解呢?

uint16_t lastCount; ------> uint16_t AMStandard$lastCount;
$是什么东西呢,在刚才的[Tinyos-help]中有提及:
“$ is an almost never used but legal character in C identifiers”
其实"AMStandard$lastCount"在C语言中完全就是合法的标志符,你可以写一个测试程序:
#include
int main(int _argc, char* _argv[])
{
int AMStandard$lastCount = 215;
printf("AMStandard$lastCount:%d\n", AMStandard$lastCount);
return 0;
}
gcc编译后运行是通过的嘛。只是我们平时很少用来着。

这里你是否想到lastCount为什么经过nesC编译后要加一个所谓的“AMStandard$”的前缀(其实AMStandard$lastCount是一个变量,是个整体)呢?我看来,这就是应该算是刚提出的问题“而且只能由其所属的组件直接访问”的含义了吧。你要访问lastCount,现在编译器把它生成带“AMStandard$”前缀的变量,而没有生成带““google$”的前缀”,你说谷歌“google”组件里也有这个lastCount,或者百度baidu也有,但他们被编译没生成AMStandard$lastCount啊,至多根据规则生成google$lastCount或者baidu$lastCount啊(开个玩笑)。又因为“组件之间的变量越权访问检查是在编译过程中完成的”,所以,这样就不能相互乱访问别的组件里的帧变量了,也就是私有了,当然也就是“只能由其所属的组件直接访问”了。当然,帧里的变量都是全局变量,那么当然自然就“帧的预分配可以防止与动态分配相关的额外开销,并且可以避免与指针相关的错误。”了。不过,要是你有恶意的话,你闲着蛋疼修改app.c文件的这些变量,想想会有什么结果呢,呵呵,当然,其实这一点意思都没有。

app.c生成的带$的函数是不是一个道理呢?动下脑筋吧.

但是command bool Control.init()中的
result_t ok1;
result_t ok2;
却并不属于所谓的帧。它们只是普通C语言函数中的局部变量,当然不是全局变量。生成的app.c已经说得足够清楚了。


参考资料:
[1] https://www.millennium.berkeley.edu/pipermail/tinyos-help/2004-July/004649.html
[2] https://www.millennium.berkeley.edu/pipermail/tinyos-help/2004-July/004654.html
[3] https://www.millennium.berkeley.edu/pipermail/tinyos-help/2004-July/004657.html
[4] System Architecture Directions for Networked Sensors
--- Qiuhua Cao (qc9b@cs) Computer Science Department University of Virginia

沒有留言:

張貼留言