定义文档:
1.
PROCESS(hello_world_process, "Hello world process");
PROCESS_THREAD(hello_world_process, ev, data)
{
/*所有的C变量声明在ROCESS_BEGIN()之前完成。以后每次调度,都是从RPOCESS_BEGIN()开始执行了*/
/*这里有点问题,手册上说的是几个handler函数必须放在RPOCESS_BEGIN之前,我的是说有声明,再找找,确定下*/
PROCESS_BEGIN();
printf("Hello, world\n");
PROCESS_END();
}
先声明一个过程,然后开启一个线程,在线程里面,RPOCESS_BEGIN() 和 PROCESS_END必须出现,而且是成对出现的。
*
2 #define PROCESS_CONTEXT_BEGIN(p)
#deine PROCESS_CONTEXT_END(p);
这两个宏可以实现process切换,一个process运行到某个地方,需要让另外一个process来运行时,调用这两个API。
3 #define PROCESS_EXITHANDLER(handler)
当一个process退出的时候,会执行这个操纵,但是必须在PROCESS_BEGIN()之前声明。
exithandler(void)
{
printf("Process exited\n");
}
PROCESS_THREAD(example_exithandler,ev,data)
{
PROCESS_EXITHANDLER(exithandler());
PROCESS_BEGIN();
while(1) {
。。。。。
}
PROCESS_END();
}
4 #define PROCESS_POLLHANDLER(hander)
这个API和 PROCESS_EXITHANDLER()类似,
5 #define PROCESS_PT_SPAWN(pt,thread)
在当前process里面 spawn 一个新的线程。这个函数只能在protoThread里面调用,父protoThread只有在子线程执行完毕之后才能运行。
6 #define PROCESS_THREAD(name,ev,data)
定义一个process的实体。
这个宏用来定义一个process的实体(protoThread),这个process将会在任何事件发生的时候被调用,
7 #define PROCESS_WAIT_EVENT()
#define PROCESS_WAIT_EVENT_UNTIL(c)
两个都是阻塞当前process直到一个事件发生,不过后者要求 c必须为真。
8:#define PROCESS_WAIT_UNTIL(c)
等待一个条件发生。
#define PROCESS_YIELD_UNTIL(c)
阻塞当前运行的process 直到condition为真。
函数文档:
1 process_event_t process_alloc_event(void)
分配一个全局的事件号。
在contiki中,事件号大于128的是全局事件号,可以从一个process中post到另一个process中,
2 CCIF void process_exit(struct process *p)
终止一个process
可以终止当前执行的process 以及当前运行process(我觉得currently running 类似与就绪的意思)
3,void process_init(void)
初始化一个process module,
在main()函数里面被调用。
4 int process_nevents(void)
返回当前等待被processed 的事件数目。
5 CCIF void process_poll(struct process *p)
轮询process ,这个函数一般是在中断函数里面,引起一个process被轮询
6 CCIF int process_post(struct process *p,process_event_t ev,process_data_t data)
post 一个同步事件
post一个同步事件到一个或者多个processs ,处理此事件的操作将会在目标process被内核调度的时候被处理。一个事件可以被广播到所有的process,此时,所有的process都会被调度去处理这个事件。
参数p ,可以是目标process 或者PROCESS_BROADCASE。
7void process_post_synch(struct process *p,process_event_t ev,process_data_t data)
投递一个同步事件到一个process。
只在tcpip_input()中被应用。
8: int process_run(void)
运行一次系统 -------调用poll 函数 并且处理一个事件
这个函数应该被main()函数反复的调用来真正的运行contiki 操作系统,它会调用需要轮询的服务函数,然后处理一个事件,这个函数将会返回在事件队列里面等待处理的事件个数,以便当队列里面没有等待事件时,可以让cpu休眠。
9,void process_start(struct process *p ,char *arg)
启动一个process。
在process_post(),和PT_INIT里面被调用。
三;时间事件
时间事件提供了一种参数时间的事件的方式(听上去有点绕口!!)
当时间流逝到规定的值,将会有一个时间事件被投递到这个process。
系统在时钟中断里面会调用的函数:
void etimer_request_poll(void)
让时间时间感知时钟的变化 一般在时钟中断里面,当tick增加的时候,
void etimer_pending(void)
查看是否有有过期的时间事件
clock_timer_t etimer_next_expiration_time(void)
得到下一个时间事件流逝的时间
应用程序调用的函数接口:
void etimer_set(struct etimer *et,clock_timer_t interval)
设置一个时间事件
void etimer_reset(struct etimer *et)
重置一时间事件,以先前设置的时间间隔
void etimer_restart(struct etimer *et)
重新开始一个时间事件,从当前的时间点
void etimer_adjust(struct etimer *et int timediff)
调整一个时间事件的间隔时间
int etimer_expired(struct etimer *et)
检查是时间事件是否超时
clock_time_t etimer_expiration_time(struct etimer*et)
得到当前时间事件流逝的时间
clock_time_t etimer_start_time(struce etimer *et)
得到时间事件的起始时间
void etimer_stop(struct etimer *et)
停止挂起的时间事件
四:Argument buffer
argument buffer 是静态分配的,全局的,任何process都可以访问的。
函数:
char *arg_alloc(char size)
分配一个argument buffer
arg_free(char *arg)
回收....
arg_alloc 分配不能超过128字节。
arg_free 回收空间。如果给予的是其他指针,也是安全的。
五:contiki program loader
contiki program loader 适用于装载和开启程序的一个抽象接口。
模型: The contiki ElF loader
contiki program loader 链接,重定位,装载ELF格式的目标文件到一个正在运行的contiki系统。
数据结构: struct dsc
定义:#define DSC(dscname,description,prgname,process,icon) CLIF const struct dsc dscname={ description,prgname,icon}
The program description structure
DSC 结构是用来描述程序的,它包括一个描述程序的串,在硬盘上文件的名字,一个位图图标,还有一个文本版的同样的icon。
DSC被保存在一个可以被程序加载的文件里,如“Directory”应用,它读取所有的硬盘上的所有DSC文件,并且呈现出一个图标和描述在一个窗口里。
六:protothread semaphores
定义了一组信号量操作,值得注意的是,所有的信号量相关数据要声明为全局的变量,protoThread是不保存局部变量的。
七: 时钟库
时钟库是介于contiki和平台特定时钟功能的接口。
时钟库实现了一个简单功能:衡量时间,另外,时钟库提供了一个宏,CLOCK_SECOND,代表一秒的系统时间。
时钟库很多时候并不是直接使用的,而是在时间事件和时间库里面会用到。
void clock_init(void)
初始化时钟库,在main()函数里面调用。
clock_time_t clock_time(void)
得到当前的时钟时间,单位是ticks,
八:多线程库
基于事件驱动的contiki核不直接支持多线程,而是将可抢占的多线程被设计为一个库,是可选择的部分链接到应用上去。
这个库有两部分组成,平台无关的部分,对于所有的平台都是一样的;和平台相关的部分,这部分必须根据平台编写,才能使多线程运行。
函数: void mt_init(void)
初始化多线程库
void mt_remove(void)
卸载和清空 库
void mt_start(struct mt_thread *thread,void(*function)(void*),void *data)
开启一个多线程
void mt_exec(struct mt_thread *thread)
执行部分现成???
void mt_yield(void)
自愿的放弃处理器
void mt_exit(void)
线程退出
void mt_stop(struct mt_thread *thread)
停止一个线程。
void mt_exec(struct mt_thread *thread)
这个函数被contiki process 调用去执行一个线程,这个函数不会返回直到线程退出或者被抢占。
九: Architecture support for multi-threading
contiki 的多线程库需要一些architecture specific 支持去seting up 和切换栈,这种支持需要四个栈操作函数:
mtarch_start():为一个新线程开辟栈结构
mtarch_exec(): 在一个线程的栈里面切换
mtarch_yield(): 在一个线程栈里面恢复内核栈
mtarch_stop(): 清空一个线程的栈
另外,两个控制抢占的函数必须实现:
mtarch_pstart() 和 mtarch_pstop(),如果不需要抢占,这两个函数可以被实现为空函数,最后mtarch_init() 被mt_init()调用。并且可以用于初始化时钟中断??or any other mechanisms required for correct operation of the architecture specific support funcions while mtarch_remove()is called by mt_remove() to clean up those resources.
函数: void mtarch_init(void)
为多线程初始化相应的architecture specific support functions
void mtarch_remove(void)
卸载函数库并清空
mtarch_start(struct mtarch_thread *thread,void(*function)(void*data),void *data)
当线程开始的时候,为其setup 栈结构
void mtarch_exec(struct mtarch_thread*thread)
开始执行一个线程
void mtarch_yield(void)
放弃cpu
void mtarch_stop(struct mtarch_thread *thread)
清空一个线程的栈空间
函数文档:
void mtarch_exec(struct mtarch_thread *thread)
启动一个线程
这个函数被mt_exec()调用,开启一个多线程服务,这个函数应该切换线程的栈,并且不会返回直到明确的调用yielded( mt_yield())或者直到被抢占。
void mtarch_init(void)
mt_init() 调用此函数,
。。。。。。。
多线程函数库提供的是接口,下面是需要自己实现的部分,这些接口会调用你实现的部分,这个多线程函数库的构架。
十:EEPROM API
EEPROM API 为EERPOM access on contiki platforms 提供了一个通用的接口。
一个带有EERPOM的平台必须实现这些API。
函数:
void eeprom_write(eeprom_addr_t addr,unsigned char *buf,int size)
向EEPROM 写入buffer
void eeprom_read(eeprom_addr_t addr,unsigned char *buf,int size)
从EEPROM中读取数据
void eeprom_init(void)
初始化EEPROM 模型
十一:Radio API
无线模型定义了一些了的函数,无线设备驱动必须要实现的。
十二:contiki ELF loader
contiki ELF loader 链接 、重定位、装载ELF 目标文件到一个运行的contiki系统中。
一个ELF文件包含了一个单独可执行文件或者一个编程模型。这个文件包含了程序代码和程序数据,还有一些信息关于怎样连接 重定位 装载 这个程序到运行的系统中。
ELF文件是由一系列的域组成的,包括 代码域 数据域 重定位信息 也包括一些调试信息。
为了连接和重定位一个ELF文件,contiki ELF loader 首先擦除ELF文件结构去找到合适ELF域,然后分别为程序代码和数据在ROM和RAM上分配内存,分配内存后,contiki ELF loader开始重定位ELF文件中的代码。
函数:elfloader_init(void)
elfloader 初始化函数
elflooader_load(int fd)
装载和重定位一个ELF文件。此ELF文件必须用cfs_open()打开。
十三:architecture specific functionality for the ELF loader
The architecture specific functionality for the ELF loader has to be implemented for each processor type contiki runs on.
由于ELF格式对不同的处理器有细微的差别,contiki的ELFloader分成两部分,
真正的ELF装载器模型 和与结构相关的部分。与体系结构相关的部分来处理内存分配,代码和数据重定位,将重定位的ELF代码写到代码空间。
将contiki的ELF 装载移植到新的处理器,这个部分是必须实现的。
函数: void *elfloader_arch_allocate_ram(int size)
allocate RAM for a new module
void *elflloader_arch_allocate_rom(int size)
allocate program memory for a new module.
void elfloader_arch_relocate(int fd,unsigned int sectionoffset,char *sectionaddr,struct elf32_rela *rela,char *ddr)
实现重定位。
void elfloader_arch_write_rom(int fd,unsigned short textoff,unsigned int size,char *mem)
写入只读内存(如代码段)
函数文档:
void *elfloader_arch_allocate_ram(int size)
为新的模型分配RAM空间
这个函数被contiki ELF loader 调用为模型的装入分配RAM空间 。
void *elflloader_arch_allocate_rom(int size)
这个函数被contiki ELF loader 调用为模型的装入分配ROM空间
void elfloader_arch_relocate(int fd,unsigned int sectionoffset,char *sectionaddr,struct elf32_rela *rela,char *ddr)
contiki ELF loader 调用此函数为代码或数据实现重定位。重定位的地址是由contiki ELF loader 根据ELF文件中的信息计算出来的。
这个函数的职责是patch 执行代码,contiki 传递一个指针到ELF32结构,这个结构包含了怎样去patch 代码的信息,这些信息在不同的处理器上是不同的。
void elfloader_arch_write_rom(int fd,unsigned short textoff,unsigned int size,char *mem)
contiki ELF loader 调用这个函数将代码段写到内存空间,这个函数是当所有的重定位完成的时候才调用的。
十四:ProtoThread
ProtoThread 是一中轻量级无栈线程,示威内存严重受限的系统设计的,如deeply 嵌入式系统,或者传感器网络节点。
ProtoThread为事件驱动的系统提供了线性的代码执行,ProtoThread可以在有和没有RTOS的情况下使用。
ProtoThread 是种极端轻量级,无栈的线程 ,它能提供一个阻塞的切换在事件驱动机制的系统上,不需要在每个任务栈上,ProtoThread的目的是实现一系列连续的操作在没有付诸的状态机的情况下或full multi-threading ,protoThread提供了条件阻塞。
处在一个纯的事件驱动的系统上的ProtoThread优势是:它提供了连续的代码结构运行阻塞功能,在纯的事件驱动机制系统上,阻塞必须手动的实现通过将函数分成两半。一半是阻塞前部分,另一半是用于阻塞后调用。这使得使用控制结构如if() 条件 和 while()循环很难实现。
ProtoThread 优越于普通线程在于:它不需要一个单独的栈,在资源受限的系统里,分配多个任务栈会消耗掉很多可用的内存空间,相反,每个protoThread只需要2到12字节,依据不同的体系结构。
主要特点:
没有与体系结构相关的代码,是纯C实现的
不要是使用error-prone(易于出错) 函数,如:longjmp()
使用很少ram空间,每个protoThread只需要2个字节
可用于有或无 OS的场所
Provides blocking wait without full multi-thread or stack-switch
protoThread API由四个基本的操作组成:
初始化:PT_INIT();
执行: PT_BEGIN();
条件阻塞:PT_WAIT_UNTIL()
退出: PT_END()
在这几个操作之上,产生了两个方便的函数,reversed 条件阻塞 PT_WAIT_WHILE(),和protoThread 阻塞 PT_WAIT_THREAD()
所有的protoThread运行在同一个栈上,protoThread之间的切换是通过栈的rewinding(回卷)实现的,((应该是压栈,弹栈实现的))。
一个protoThread在运行在一个单独的c函数里面,不能跨越到其他函数里面。一个protoThread可以调用普通的c函数,但是不能阻塞在里面,Blocking inside nested function call is instead made by spawping a separate protothread for each potentially blocking function.这样做的优势是阻塞是很明确的:程序确切的知道哪些函数是阻塞的,哪些是不阻塞的。
protoThreads 类似与非对称的协程,主要的不同点在于协程为每个协程分配一个单独的栈。而protoThread是没有栈的。
调度部分:
protoThread 是由它所在的函数里面反复调用驱动起来的。每次函数被调用,这个protoThread就会运行,直到它被阻塞或者退出,protoThread的调度是由应用程序使用protoThread完成的。
实现:
protoThread 是用local continuations 实现的。一个 local continuation 代表在程序里面一个特定的执行状态,但是不提供任何调用历史和局部变量。一个local continuation 可以被设置在特定的函数里面去捕获这个函数的状态。在一个local continuation被设置后,可以被唤醒,去恢复到local continuation 被设置地方的状态。
local continuation 可以使用多种方式实现:
1,使用与机器相关的汇编代码
2,使用标准的c结构
3,使用编译器扩展 (by using compiler externsions)
第一种方法通过保存和恢复处理器状态,除了堆栈指针外,每个protoThread还需要16到32字节的内存空间,准确需要的内存大小是由体系结构决定的。
标准C实现,每个protoThread需要两个字节state .利用c switch() 声明以一种不明显的方式,然而这种方式将会对代码产生一点限制:在使用protoThread的代码里面,不能使用switch()声明它本身。(????)
特定的编译器有C扩展性,GCC支持label pointers 可用于这个目的,通过这种方式,每个protoThread需要4个字节。
初始化: #define PT_INIT(pt)
初始化protoThread
#define PT_THREAD(name_args)
声明一个protoThread
#define PT_BEGIN(pt)
声明一个里面包含c函数的protoThread的起始。
#define PT_END(pt)
阻塞等待:
#define PT_WAIT_UNTIL(pt,condition)
阻塞等待直到条件为真。
#define PT_WAIT_WHILE(pt,cond)
当条件为真时,阻塞等待。
分层 protoThreads:
#define PT_WAIT_THREAD(pt,thread)
阻塞等待直到子protoThread完成
#define PT_SPAWN(pt,child,thread)
卵生一个子线程,并等待直到它退出。
退出和重启:
#define PT_RESTART(pt)
重启一个protoThread
#define PT_EXIT(pt)
退出protoThread
调用一个 protoThread
#define PT_SCHEDULE(f)
调度一个protoThread
yielding from a protothread
#define PT_YILELD(pt)
yield 当前protoThread
#define PT_YIELD_UNTIL(pt,cond)
yield 当前线程直到条件发送。
#define PT_RESTART(pt)
这个宏将会阻塞并导致当前运行的protoThread 重新从PT_BEGIN()处执行。
#define PT_SECHEDULE(f)
调度一个protoThread,返回非零表示protoThread在运行,0 表示protoThread已经退出。
十五:contiki文件系统接口
contiki 文件系统接口定义了一组抽象的API为读取目录 读取文件和写文件。
CFS API 接口比较简单,根据POSIX file API 为模型,做了少量的改变。
函数:
CCIF int cfs_open(const char *name,int flags)
CCIF void cfs_close(int fd)
CCIF int cfs_read(int fd,char *buf,unsigned int len)
CCIF int cfs_wirte(int fd,char *buf,unsigned int len)
CCIF int cfs_seek(int fd, unsigned int offset)
CCIF int cfs_opendir(struct cfs_dir *dirp,const,char *name)
为读取目录项打开目录。
CCIF int cfs_closedir(struct cfs_dir *dirp)
十六:CTK application functions
对此不了解,先略过
十七: Timer library
contiki 内核不为timed tevents 提供支持。
而是,当一个应用需用使用timers,需要明确的使用timer library
timer— library 提供了 设置 重置 重启 定时器,已经测试一个定时器是否超时的函数,应用程序必须手动的检测它的定时器是否超时,而不是自动检测的。
十八:uip 配置函数
十九:Protosocket library
protoSocket library 为uIP提供了一个接口,类似与传统的BSD套接字接口。不想普通的uip 事件驱动接口,使用protoSocket library 的程序 可以连续的执行,而不必有明确的状态机。
protoSocket 只工作在TCP连接中。
protoSocket 使用Protothreads protothreads 提供连续的控制flow,这使得protosocket 在内存方面是轻量级的,并且继承了protothread 有限的功能。每个protosocket lives only within a single function block。Automatic variables (stack variables) are not necessarily retained across a protosocket library function call.
由于protosocket library 使用了protothread,局部变量will not always be saved across a call to a protosocket library function ,所以在处理局部变量时要特别小心。
protosocket library 为发送数据提供了一些函数,使得其不用考虑重传和确认,以及读取数据的函数,不用考虑数据的TCP分段。
由于每个protosocket 都是作为一个protothread来运行,protosocket 需要调用PSOCK_BEGIN()在函数的开始的部分,同样,在结束的时候需要调用PSOCK_EXIT();
{
小结:process protosocket 都是使用protothread 来实现的。
#define PSOCK_BEGIN(psock) PT_BEGIN(&((psock)->pt))
}
二十: 匿名尽最大努力交付 本地广播
未完,东西有点多,看了这么多还有很多没有理解,先弄懂了再继续。