四.Contiki之Protothread机制
来源:互联网 发布:获取局域网mac地址命令 编辑:程序博客网 时间:2024/05/20 12:47
1.main函数分析
1.1 main函数源码
#include <stm32f10x.h>#include <stm32f10x_dma.h>#include <stdint.h>#include <stdio.h>#include <debug-uart.h>#include <sys/process.h>#include <sys/procinit.h>#include <etimer.h>#include <sys/autostart.h>#include <clock.h>int main(){ dbg_setup_uart(); //串口初始化,与硬件相关 usart_puts("Initialising\n"); //向串口打印字符串"Initialising"//详见1.2时钟初始化,与硬件相关 clock_init();//详见1.3进程初始化 process_init();//启动etimer_process(3.5 etimer_process) process_start(&etimer_process, NULL);//详见1.4启动指针数组autostart_processes[]里的所有进程【autostart_start()函数中包含了process_start(),详见2】 autostart_start(autostart_processes); while (1) {//执行完所有needspoll为1的进程及处理完所有队列,详见3 do { } while (process_run() > 0);//4.3 process_run() } return 0;}
1.2 时钟初始化clock_init()
void clock_init(){//详见① SysTick_Config函数 SysTick_Config(72000000 / CLOCK_SECOND);}/*CLOCK_SECOND宏定义*/#define CLOCK_CONF_SECOND 10#ifdef CLOCK_CONF_SECOND #define CLOCK_SECOND CLOCK_CONF_SECOND#else #define CLOCK_SECOND (clock_time_t)32#endif//①SysTick_Config函数(与硬件有关)static __INLINE uint32_t SysTick_Config(uint32_t ticks){ if (ticks > SysTick_LOAD_RELOAD_Msk) return (1);/* Reload value impossible */ SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;/* set reload register */ NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);/* set Priority for Cortex-M0 System Interrupts */ SysTick->VAL = 0;/* Load the SysTick Counter Value */ SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk;/* Enable SysTick IRQ and SysTick Timer */ return (0);/* Function successful */}
1.3 进程初始化
/****进程初始化****/void process_init(void){ /*初始化事件队列*/ lastevent = PROCESS_EVENT_MAX; nevents = fevent = 0;//4.3.3中nevents和fevent的定义 #if PROCESS_CONF_STATS process_maxevents = 0; #endif process_current = process_list = NULL;//初始化进程链表}
1.4 autostart_start函数
//autostart_start(autostart_processes); // autostart_processes由宏AUTOSTART_PROCESSES定义【详见博文:http://blog.csdn.net/u012325020/article/details/52832659】void autostart_start(struct process *const processes[]){ int i; for(i = 0; processes[i] != NULL; ++i) { process_start(processes[i], NULL);//进程的启动,见4.2 PRINTF("autostart_start: starting process '%s'\n", processes[i]->name); }}
2.进程运行过程
进程运行过程大致为:process_start——>process_post_synch——>call_process——>exit_process
2.1 process_start函数
/*******启动一个进程********/void process_start(struct process *p, const char *arg) //可以传递arg给进程p,也可以不传,直接“NULL”{ struct process *q;/*参数验证:确保进程不在进程链表中*/ for(q = process_list; q != p && q != NULL; q = q->next); if(q == p) { return; }/*把进程加到进程链表首部*/ p->next = process_list; process_list = p; p->state = PROCESS_STATE_RUNNING;/*将p->pt->lc设为0,使得进程从case 0开始执行(PT_INIT()包含在PROCESS_END()里面【详见博文:http://blog.csdn.net/u012325020/article/details/52832659】)*/ PT_INIT(&p->pt); PRINTF("process: starting '%s'\n", PROCESS_NAME_STRING(p));//详见2.2 :给进程传递一个PROCESS_EVENT_INIT事件,让其开始执行 process_post_synch(p, PROCESS_EVENT_INIT, (process_data_t)arg);}
2.2 process_post_synch函数
//process_post_synch(p, PROCESS_EVENT_INIT, (process_data_t)arg);void process_post_synch(struct process *p, process_event_t ev, process_data_t data) //typedef void * process_data_t{//相当于PUSH,保存现场process_current// struct process *process_current = NULL; struct process *caller = process_current; call_process(p, ev, data);//详见2.3//相当于POP,恢复现场process_current process_current = caller; }//这里先process_current指针保存到caller,call_process()后再复原process_current。这说明在call_process()方法内,process_current指针被改变了。
2.3 call_process函数
//call_process(p, PROCESS_EVENT_INIT, (process_data_t)arg);static void call_process(struct process *p, process_event_t ev, process_data_t data){ int ret;#if DEBUG if(p->state == PROCESS_STATE_CALLED) { printf("process: process '%s' called again with event %d\n", PROCESS_NAME_STRING(p), ev); }#endif /* DEBUG *///process_start()中将process的状态设置为了running,此处确保状态为running并且process所指的函数不为空 if((p->state & PROCESS_STATE_RUNNING) && p->thread != NULL) //thread是函数指针 { PRINTF("process: calling process '%s' with event %d\n", PROCESS_NAME_STRING(p), ev);//打印进程名和事件号// process_current指向了当前处理的进程,所以在process_post_synch()调用完call_process()后要将process_current改回原值 process_current = p; p->state = PROCESS_STATE_CALLED; ret = p->thread(&p->pt, ev, data); //才真正执行PROCESS_THREAD(name, ev, data)定义的内容 if(ret == PT_EXITED || ret == PT_ENDED || ev == PROCESS_EVENT_EXIT) { exit_process(p, p); //如果返回值表示退出、结尾或者遇到PROCESS_EVENT_EXIT,进程退出 } else { p->state = PROCESS_STATE_RUNNING; //进程挂起等待事件 } }}
2.4 exit_process函数
//struct process *p 指要退出的进程//struct process *fromprocess 指当前的进程 ?//exit_process(p, p)static void exit_process(struct process *p, struct process *fromprocess){ register struct process *q;//把process指针存入寄存器 struct process *old_current = process_current;//先保存current指针,用完再换回来 PRINTF("process: exit_process '%s'\n", PROCESS_NAME_STRING(p));/*参数验证:确保要退出的进程在进程链表中*/ for(q = process_list; q != p && q != NULL; q = q->next); if(q == NULL) { return; } if(process_is_running(p)) //return p->state != PROCESS_STATE_NONE; { p->state = PROCESS_STATE_NONE;/*向所有进程发一个同步事件PROCESS_EVENT_EXITED,通知他们将要退出,使得与该进程相关的进程进行相应处理*/ for(q = process_list; q != NULL; q = q->next) { if(p != q) { call_process(q, PROCESS_EVENT_EXITED, (process_data_t)p); } } if(p->thread != NULL && p != fromprocess) /*退出的进程不是当前进程*/ {/* Post the exit event to the process that is about to exit. */ process_current = p;//给进程p传递一个PROCESS_EVENT_EXIT事件,通常用于退出死循环(如PROCESS_WAIT_EVENT函数),从而使其从主体函数退出 p->thread(&p->pt, PROCESS_EVENT_EXIT, NULL); } }/*将进程p从进程链表删除*/ if(p == process_list) //进程p恰好在进程链表首部 { process_list = process_list->next; } else //进程p不在进程链表首部 { for(q = process_list; q != NULL; q = q->next) //遍历进程链表,寻找进程p,删除之 { if(q->next == p) { q->next = p->next; break; } } } process_current = old_current;}
3.进程处理
3.1 process_run函数
/*process_run函数处理系统所有needspoll标记为1的进程及处理事件队列的下一个事件*/static volatile unsigned char poll_requested; //全局静态变量,标识系统是否有needspoll为1的进程int process_run(void){ if (poll_requested) //进程链表有needspoll为1的进程 { do_poll();//见3.2 } do_event();//见3.3 return nevents + poll_requested;//若和为0,则表示处理完系统的所有事件,并且没有needspoll为1的进程}
3.2 do_poll函数
/*复位全局变量poll_requested,遍历整个进程链表,将needspoll标记为1的进程投入运行,并将相应的needspoll复位*/static void do_poll(void){ struct process *p; poll_requested = 0;//复位全局变量 for (p = process_list; p != NULL; p = p->next)//处理所有needspoll为1的进程 { if (p->needspoll)//将needspoll为1的进程投入执行 { p->state = PROCESS_STATE_RUNNING; p->needspoll = 0; call_process(p, PROCESS_EVENT_POLL, NULL); } }}
3.3 do_poll函数
/*do_event处理事件队列的一个事件,有两种事件需特殊处理:PROCESS_BROADCAST和PROCESS_EVENT_INIT。前者是广播事件,需处理所有进程,后者是初始化事件,需将进程状态设为PROCESS_STATE_RUNNING。*/static process_num_events_t nevents; /*事件队列的总事件数*/static process_num_events_t fevent; /*指向下一个要传递的事件的位置*/static struct event_data events[PROCESS_CONF_NUMEVENTS]; /*事件队列,用数组存储,逻辑上是环形队列*/static void do_event(void){//以下3个变量恰为struct event_data的成员,用于暂存即将处理(fevent事件)的值(2.1event_data的定义) static process_event_t ev; static process_data_t data; static struct process *receiver; static struct process *p; if (nevents > 0) {/*提取将要处理事件的成员变量*/ ev = events[fevent].ev; data = events[fevent].data; receiver = events[fevent].p; fevent = (fevent + 1) % PROCESS_CONF_NUMEVENTS; //更新fevent(事件队列被组织成环形队列,所以取余数) --nevents; if (receiver == PROCESS_BROADCAST) //如果事件是广播事件PROCESS_BROADCAST,则处理所有进程 { for (p = process_list; p != NULL; p = p->next) { if (poll_requested) { do_poll(); } call_process(p, ev, data); } } else { if (ev == PROCESS_EVENT_INIT) //若事件是初始化,设置进程状态,确保进程状态为PROCESS_STATE_RUNNING { receiver->state = PROCESS_STATE_RUNNING; } call_process(receiver, ev, data); } }}
0 0
- 四.Contiki之Protothread机制
- Contiki:Protothread切换机制理解
- Protothread机制文档(contiki-2.6\doc\pt-doc.txt)
- [Contiki系列论文之3]Protothread:简化内存受限系统上的事件驱动编程
- 从ProtoThread开始到contiki的学习
- 解析Contiki系统protothread的预编译C代码
- contiki系统分析四:内存分配
- contiki任务调度机制分析
- 二.Contiki之事件
- contiki 之 button sensor
- Contiki之初步
- contiki之list(1)
- contiki之list(2)
- contiki之nbr_table.c
- contiki 之 button sensor
- contiki 源码分析之mac层(四)(core / net / mac)
- Contiki
- contiki学习笔记之hello_world
- 数组赋值
- HTML/CSS基础篇
- 山东大学电子设计与飞思卡尔协会培训1——51单片机
- 初识linux设备驱动开发
- 使用Jquery开源插件实现文件上传(带进度条)
- 四.Contiki之Protothread机制
- 洛谷 P1137 旅行计划
- 不要给真理, 要给解决方案!
- hdu 5980 Find Small A
- NO.4 鼠标的绘图操作
- JSTL标签库
- Coding和Git的环境搭建
- 1051. 复数乘法 (15)
- 1、Java垃圾回收