




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、实验题目:实验15基于IPv6模块的进程间交互的实验 实验时间:一、 实验目的:了解Cygwin开发环境及Contiki系统相关内容。了解Contiki系统进程间交互的理论掌握IPv6模块的编程及下载使用方法。二、 实验原理及程序分析:1、Contiki 中事件驱动和protothread机制Contiki的两个主要机制:事件驱动和protothread机制,前者是为了降低功耗,后者是为了节省内存。事件驱动嵌入式系统常常被设计成响应周围环境的变化,而这些变化可以看成一个个事件。事件来了,操作系统处理之,没有事件到来,就跑去休眠了(降低功耗),这就是所谓的事件驱动,类似于中断。1、 事件结构体事
2、件也是Contiki重要的数据结构,其定义如下:struct event_dataprocess_event_t ev;process_data_t data;structprocess*p;typedef unsigned char process_event_t;typedef void * process_data_t;各成员变量含义如下:ev-标识所产生事件data-保存事件产生时获得的相关信息,即事件产生后可以给进程传递的数据p-指向监听该事件的进程2、 事件分类事件可以被分为三类:时钟事件(timer events)、外部事件、内部事件。那么,Contiki核心数据结构就只有进程和
3、事件了,把etimer理解成一种特殊的事件。3、 事件队列Contiki用环形队列组织所有事件(用数组存储),如下:static struct event_data eventsPROCESS_CONF_NUMEVENTS;图示事件队列如下:4、 系统定义的事件系统定义了10个事件,源码和注释如下:#ifndef PROCESS_CONF_NUMEVENTS#define PROCESS_CONF_NUMEVENTS 32#endif#define PROCESS_EVENT_NONE 0x80 / 函数dhcpc_request 调用handle_dhcp(PROCESS_EVENT_NON
4、E,NULL)#define PROCESS_EVENT_INIT 0x81 /启动一个进程process_start,通过传递该事件#define PROCESS_EVENT_POLL 0x82 /在PROCESS_THREAD(etimer_process, ev, data)使用到#define PROCESS_EVENT_EXIT 0x83 /进程退出,传递该事件给进程主体函数thread#define PROCESS_EVENT_SERVICE_REMOVED 0x84#define PROCESS_EVENT_CONTINUE 0x85 /PROCESS_PAUSE宏用到这个事件#
5、define PROCESS_EVENT_MSG 0x86#define PROCESS_EVENT_EXITED 0x87 /进程退出,传递该事件给其他进程#define PROCESS_EVENT_TIMER 0x88 /etimer到期时,传递该事件#define PROCESS_EVENT_COM 0x89#define PROCESS_EVENT_MAX 0x8a /*进程初始化时,让lastevent=PROCESS_EVENT_MAX,即新产生的事件从0x8b开始,函数process_alloc_event用于分配一个新的事件*/注:PROCESS_EVENT_EXIT 与PRO
6、CESS_EVENT_EXITED区别事件PROCESS_EVENT_EXIT 用于传递给进程的主体函数thread,如在exit_process函数中的p->thread(&p->pt, PROCESS_EVENT_EXIT, NULL)。而PROCESS_EVENT_EXITED用于传递给进程,如call_process(q, PROCESS_EVENT_EXITED, (process_data_t)p)。(助记:EXITED 是完成式,发给进程,让整个进程结束。而g一般式EXIT,发给进程主体thread,只是使其退出thread)5、 一个特殊事件如果事件结构体e
7、vent_data的成员变量p指向PROCESS_BROADCAST,则该事件是一个广播事件。在do_event函数中,若事件的p指向的是PROCESS_BROADCAST,则让进程链表process_list所有进程投入运行。部分源码如下:#define PROCESS_BROADCAST NULL /广播进程/*保存待处理事件的成员变量*/ev = eventsfevent.ev;data = eventsfevent.data;receiver=eventsfevent.p;if(receiver=PROCESS_BROADCAST)for(p = process_list; p !=
8、NULL; p = p->next)if(poll_requested)do_poll();call_process(p, ev, data);protothread 机制1. 概述protothread是contiki的进程控制模型,是contiki关于进程的精华知识。传统的操作系统使用栈保存进程上下文,每个进程需要一个栈,这对于内存极度受限的传感器设备将难以忍受。protothread机制恰解决了这个问题,通过保存进程被阻塞处的行数(进程结构体的一个变量,unsiged short类型,只需两个字节),从而实现进程切换,当该进程下一次被调度时,通过switch(_LINE_)跳转到刚
9、才保存的点,恢复执行。整个Contiki只用一个栈,当进程切换时清空,大大节省内存。2. 特点protothread(- Lightweight, Stackless Threads in C)最大特点就是轻量级,每个protothread不需要自己的堆栈,所有的protothread使用同一个堆栈,而保存程序断点用两个字节保存被中断的行数即可。具体特点如下:Very small RAM overhead - only two bytes per protothread and no extra stacksHighly portable - the protothreads library
10、is 100% pure C and no architecture specific assembly codeCan be used with or without an OSProvides blocking wait without full multi-threading or stack-switchingFreely available under a BSD-like open source licenseprotothread机制很早就有了,Contiki OS只要运用这种机制,protothread机制还可以用于以下情形:Memory constrained systems
11、Event-driven protocol stacksSmall embedded systemsSensor network nodesPortable C applications3. 编程提示谨慎使用局部变量。当进程切换时,因protothread没有保存堆栈上下文(只使用两个字节保存被中断的行号),故局部变量得不到保存。这就要求使用局部变量要特别小心,一个好的解决方法,使用局部静态变量(加static修饰符),如此,该变量在整个生命周期都存在。4. 调度A protothread is driven by repeated calls to the function in which
12、 the protothread is running.Each time the function is called, the protothread will run until it blocks or exits. Thus the scheduling ofprotothreads is done by the application that uses protothreads.5. protothread模型之代码分析6. 当第一次执行这个函数时,便会执行PT_YIELD_FLAG = 1。因为(process_pt)->lc被初始化为0,所以就直接输出hello wor
13、ld。当进入到while循环时:while(1)/* Wait for an event. */PROCESS_WAIT_EVENT();doPT_YIELD_FLAG = 0;(process_pt)->lc = _LINE_; case _LINE_: ;if(PT_YIELD_FLAG = 0)return 1; while(0);/* Got the timer's event */当这个process收到一个事件再次被执行的时候,首先会进行初始化,让PT_YIELD_FLAG = 1,接着进入switch语句,因为(process_pt)->lc已经被保存,所以这
14、次process直接跳到了上次的执行结束的地方重新开始执行了,因为PT_YIELD_FLAG 已经是1了,所以就不会return了。就会继续执行上次执行的代码。所以(process_pt)->lc相当于保持了现场,下次函数再运行的时候就可以通过switch语句迅速找到执行代码语句的入口,恢复现场,继续执行。执行完毕后,接着while循环,return后,再等待事件,一直循环。2、Contiki 中进程概念进程结构体进程结构体源码如下:struct processstructprocess*next;/指向下一个进程/*进程名称*/#if PROCESS_CONF_NO_PROCESS_N
15、AMES#define PROCESS_NAME_STRING(process) ""#elseconstchar*name;#define PROCESS_NAME_STRING(process) (process)->name#endifPT_THREAD(*thread)(struct pt *, process_event_t, process_data_t); /structpt pt; /unsigned char state; /unsigned char needspoll; /;1、 进程名称运用C 语言预编译指令,可以配置进程名称,宏PROCESS
16、_NAME_STRING(process)用于返回进程process名称,若系统无配置进程名称,则返回空字符串。在以后讨论中,均假设配有进程名称。2、 PT_THREAD宏PT_THREAD宏定义如下:#define PT_THREAD(name_args) char name_args故“PT_THREAD(*thread)(struct pt *, process_event_t, process_Data_t); ”语句展开如下:char (*thread)(struct pt *, process_event_t, process_data_t);声明一个函数指针thread,指向的是
17、一个含有3个参数,返回值为char类型的函数。这是进程的主体,当进程执行时,主要是执行这个函数的内容。另,声明一个进程包含在宏PROCESS(name, strname) 里,通过宏AUTOSTART_PROCESSES(.) 将进程加入自启动数组中。3、 Ptpt结构体一步步展开如下:struct ptlc_t lc;typedef unsigned short lc_t;如此,可以把struct pt pt直接理解成unsigned short lc,以后如无特殊说明,pt就直接理解成lc。lc(localcontinuations)用于保存程序被中断的行数(只需两个字节,这恰是proto
18、thread轻量级的集中体现),被中断的地方,保存行数(s=_LINE_)接着是语句case _LINE_。当该进程再次被调度时,从PROCESS_BEGIN()开始执行,而该宏展开含有这条语句switch(process_pt->pt),从而跳到上一次被中断的地方(即case_LINE_),继续执行。4、 进程状态进程共3个状态,宏定义如下:#define PROCESS_STATE_NONE 0 /*类似于Linux系统的僵尸状态,进程已退出,只是还没从进程链表删除*/#define PROCESS_STATE_RUNNING 1 /*进程正在执行*/#define PROCESS_
19、STATE_CALLED 2 /*实际上是返回,并保存lc值*/5、 Needspoll简而言之,needspoll为1的进程有更高的优先级。具体表现为,当系统调用process_run()函数时,把所有needspoll标志为1的进程投入运行,而后才从事件队列取出下一个事件传递给相应的监听进程。与needspoll相关的另一个变量poll_requested,用于标识系统是否存在高优先级进程,即标记系统是否有进程的needspoll为1。static volatile unsigned char poll_requested;进程链表基于上述分析,将代码展开或简化,得到如下进程链表proce
20、ss_list:创建进程及启动进程创建进程主要由PROCESS 宏(声明进程)和PROCESS_THREAD 宏(定义进程执行主体)完成,详情前一章helloword分析,启动进程由process_start函数完成,总是把进程加入到进程链表的头部。3、进程、事件、etimer三者关系 进程process、事件event_data、etimer都是Contiki的核心数据结构,理清这三者关系,将有助于对系统的理解。事件与etimer关系事件即可以分为同步事件、异步事件,也可以分为定时器事件、内部事件、外部事件。而etimer属于定时器事件的一种,可以理解成Contiki系统把etimer单列出
21、来,方便管理(由etimer_process系统进程管理)。当etimer_process执行时,会遍历etimer链表,检查etimer是否有到期的,凡有timer到期就把事件PROCESS_EVENT_TIMER 加入到事件队列中,并将该etimer成员变量p指PROCESS_NONE。在这里,PROCESS_NONE 用于标识该etimer是否到期,函数etimer_expired会根据etimer的p是否指向PROCESS_NONE 来判断该etimer是否到期。进程与etimer关系etimer与process还不是一一对应的关系,一个etimer必定绑定一个process,但pro
22、cess不一定非得绑定etimer。etimer只是一种特殊事件罢了。进程与事件关系当有事件传递给进程时,就新建一个事件加入事件队列,并绑定该进程,所以一个进程可以对应于多个事件(即事件队列有多个事件跟同一个进程绑定),而一个事件可以广播给所有进程,即该事件成员变量p指向空。当调用do_event函数时,将进程链表所有进程投入运行。4、源码分析源码/filename:process-interact.c 进程间交互#include "contiki.h"#include <stdio.h> /* For printf() */static process_eve
23、nt_t event_data_ready;/*声明两个进程*/PROCESS(print_hello_process, "Hello");PROCESS(print_world_process, "world");AUTOSTART_PROCESSES(&print_hello_process, &print_world_process); /让该两进程自启动/*定义进程print_hello_process*/PROCESS_THREAD(print_hello_process, ev, data)PROCESS_BEGIN();st
24、atic struct etimer timer;etimer_set(&timer, CLOCK_CONF_SECOND / 10); /#define CLOCK_CONF_SECOND 10将timer的interval设为1printf("*print hello process start*n");event_data_ready = process_alloc_event(); /return lastevent+; 新建一个事件,事实上是用一组unsigned char值来标识不同事件while (1)PROCESS_WAIT_EVENT_UNTIL(
25、ev = PROCESS_EVENT_TIMER); /即etimer到期继续执行下面内容printf("Hellon");process_post(&print_world_process, event_data_ready, NULL); / 传递异步事件给print_world_process,直到内核调度该进程才处理该事件。etimer_reset(&timer); /重置定时器PROCESS_END();/*定义进程print_world_process*/PROCESS_THREAD(print_world_process, ev, data)P
26、ROCESS_BEGIN();printf("*print world process start*n");while (1)PROCESS_WAIT_EVENT_UNTIL(ev = event_data_ready);printf("worldn");PROCESS_END();进程print_hello_process一直执行到PROCESS_WAIT_EVENT_UNTIL(ev =PROCESS_EVENT_TIMER),此时etimer还没到期,进程被挂起。转去执行print_world_process,待执行到PROCESS_WAIT_EV
27、ENT_UNTIL(ev = event_data_ready)被挂起(因为print_hello_process还没post事件)。而后再转去执行系统进程etimer_process,若检测到etimer到期,则继续执行print_hello_process,打印Hello,并传递事件event_data_ready给print_world_process,初始化timer,待执行到PROCESS_WAIT_EVENT_UNTIL(while死循环),再次被挂起。转去执行print_world_process,打印world,待执行到PROCESS_WAIT_EVENT_UNTIL(ev =
28、 event_data_ready)又被挂起。再次执行系统进程etimer_process,如此反复执行。1、etimer_set函数/etimer_set(&timer, CLOCK_CONF_SECOND/10);voidetimer_set(struct etimer *et, clock_time_t interval)timer_set(&et->timer, interval); /设置定时器timer,add_timer(et); /将这个etimer加入timerlisttimer_set函数:/用当前时间初始化start,并设置间隔intervalvoi
29、dtimer_set(struct timer *t, clock_time_t interval)t->interval = interval;t->start = clock_time(); /return current_clock;clock_time_t clock_time(void)returncurrent_clock;staticvolatileclock_time_t current_clock = 0;typedef unsigned int clock_time_t;add_timer函数:/*将etimer加入timerlist,并update_time(
30、),即求出下一个到期时间next_expiration*/staticvoidadd_timer(struct etimer *timer)structetimer *t;etimer_request_poll(); /事实上执行process_poll(&etimer_process),即将进程的needspoll 设为1,使其获得更高优先级,详情见下方/*参数验证,确保该etimer不是已处理过的*/if(timer->p != PROCESS_NONE) /如果是PROCESS_NONE,则表示之前处理过,该etimer已到期(注1)for(t = timerlist; t
31、 != NULL; t = t->next)if(t = timer) /* Timer already on list, bail out. */update_time(); /即求出下一个到期时间next_expiration(全局静态变量),即还有next_expiration 时间,就有timer到期return;/*将timer加入timerlist*/timer->p = PROCESS_CURRENT();timer->next=timerlist;timerlist = timer;update_time(); /即求出下一个到期时间next_expirati
32、on(全局静态变量),即还有next_expiration 时间,就有timer到期函数etimer_request_poll直接调用process_poll,一步步展开如下:void etimer_request_poll(void)process_poll(&etimer_process);voidprocess_poll(struct process *p)if(p != NULL)if(p->state = PROCESS_STATE_RUNNING | p->state = PROCESS_STATE_CALLED)p->needspoll = 1;poll
33、_requested = 1; /全局静态变量,标识是否有进程的needspoll为12、PROCESS_WAIT_EVENT_UNTIL宏/PROCESS_WAIT_EVENT_UNTIL(ev = PROCESS_EVENT_TIMER);/*宏PROCESS_WAIT_EVENT_UNTIL,直到条件c为真时,进程才得以继续向前推进*/#definePROCESS_WAIT_EVENT_UNTIL(c) PROCESS_YIELD_UNTIL(c)#definePROCESS_YIELD_UNTIL(c) PT_YIELD_UNTIL(process_pt, c) /Yield the
34、currently running process until a condition occurs#definePT_YIELD_UNTIL(pt, cond) doPT_YIELD_FLAG = 0; LC_SET(pt)->lc); /注:保存断点,下次从这里开始执行!if(PT_YIELD_FLAG = 0) | !(cond) returnPT_YIELDED; while(0)#defineLC_SET(s) s = _LINE_; case _LINE_: /保存行数,即lc被设置成该LC_SET 所在的行从源代码可以看出,第一次执行PROCESS_WAIT_EVENT_U
35、NTIL总是被挂起(因为PT_YIELD_FLAG=0为真,会直接执行return PT_YIELDED)。 该进程下一次被调试的时候,总是从PROCESS_BEGIN()开始,而PROCESS_BEGIN 宏包含两条语句:其一,将PT_YIELD_FLAG 置1;再者,switch(pt->lc),从而跳转到case _LINE_(见上述源码的注)。接着判断if条件,此时PT_YIELD_FLAG=1,若条件为真,则不执行return,继续后续的内容,从而进程接着执行。 3、process_post函数 源码如下:/process_post(&print_world_proce
36、ss, event_data_ready, NULL); 即把事件event_data_ready 加入事件队列intprocess_post(struct process *p, process_event_t ev, process_data_t data)staticprocess_num_events_t snum;if(nevents = PROCESS_CONF_NUMEVENTS)returnPROCESS_ERR_FULL;snum = (process_num_events_t)(fevent + nevents) % PROCESS_CONF_NUMEVENTS;eventssnum.ev = ev;eventssnum.data = data;eventssnum.p = p;+nevents;#if PROCESS_CONF_STATSif(nevents > process_maxevents)pr
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 《迷网青春》观后感
- 集中式陆上风电工程初步设计
- 河道生态修复工程规划设计方案(参考模板)
- 妆容设计分享
- 校本教研同步资源-必修3单元测U2英语试卷含解析
- 宿迁泽达职业技术学院《管理会计理论与实务》2023-2024学年第二学期期末试卷
- 福建信息职业技术学院《地球系统数值模拟》2023-2024学年第二学期期末试卷
- 和田师范专科学校《电子商务网络数据库》2023-2024学年第二学期期末试卷
- 杨凌职业技术学院《进出口业务模拟操作》2023-2024学年第二学期期末试卷
- 宁夏艺术职业学院《专题设计V》2023-2024学年第二学期期末试卷
- 江苏省南京市、盐城市2025届高三年级5月第二次模拟考试政治试题及答案(南京盐城二模)
- 快递员合同协议书范本
- 互联网+农产品商业计划书
- 2025届云南省昆明市“三诊一模”高考模拟考试历史试题(含答案)
- 公司安全事故隐患内部举报、报告奖励制度
- DL-T5344-2018电力光纤通信工程验收规范
- 简明法语教程上册答案
- 过敏性紫癜教案-苏赞彩
- GB∕T 36266-2018 淋浴房玻璃(高清版)
- 外贸报关用发票、装箱单、合同、报关单模板
- 安全联锁监控系统使用说明书
评论
0/150
提交评论