contiki/platform/linux/conkiki-main.c这里是zipgateway主入口intmain(int argc, char** argv){signal(SIGINT,exit_handler );signal(SIGTERM,exit_handler );signal(SIGHUP,exit_handler );//autostart_start启动由AUTOSTART_PROCESSES(zip_process);定义的自动启动线程zip_processautostart_start(autostart_processes);}主Z/IP进程此进程启动所有其他需要的进程PROCESS(zip_process, “ZIP process”);//定义一个数组存放程序中自动运行的线程并将zip_process添加到数组中//contiki初始化完成后将自动运行该线程AUTOSTART_PROCESSES(zip_process);///////////////////////////////////////////////////////////////////////////////////////////contiki/platform/linux/conkiki-main.cvoid exit_handler(int num) {if (num SIGHUP) {process_post(zip_process,ZIP_EVENT_RESET,0);}process_post(PROCESS_BROADCAST,PROCESS_EVENT_EXIT,0);}//////////////////////////////////////contiki/core/sys/process.cintprocess_post(struct process *p, process_event_t ev, process_data_t data) CC_REENTRANT_ARG{static process_num_events_t snum;if(PROCESS_CURRENT() NULL) {PRINTF(“process_post: NULL process posts event %d to process ‘%s’, nevents %d\n”,ev,PROCESS_NAME_STRING§, nevents);} else {PRINTF(process_post: Process %s posts event %d to process %s, nevents %d\n, PROCESS_NAME_STRING(PROCESS_CURRENT()), ev, p PROCESS_BROADCAST? : PROCESS_NAME_STRING(p), nevents);}snum (process_num_events_t)(fevent nevents) % PROCESS_CONF_NUMEVENTS;events[snum].ev ev;events[snum].data data;events[snum].p p;nevents;return PROCESS_ERR_OK;}////////////////////////////////////////src/ZIP_Router.cPROCESS(zip_process, “ZIP process”);contiki/core/sys/process.h#define PROCESS(name, strname)PROCESS_THREAD(name, ev, data);struct process name { NULL, strname,process_thread_##name }contiki/core/sys/process.h#define PROCESS_THREAD(name, ev, data)static PT_THREAD(process_thread_##name(struct pt *process_pt,process_event_t ev,process_data_t data))综合上面两个#define PROCESS(name, strname)static PT_THREAD(process_thread_##name(struct pt *process_pt,process_event_t ev,process_data_t data));struct process name { NULL, strname,process_thread_##name }又有#define PT_THREAD(name_args) char name_argsPROCESS(zip_process, “ZIP process”);也就变成了static char process_thread_zip_process(struct pt *process_pt,process_event_t ev,process_data_t data);struct process zip_process { NULL, “ZIP process”,process_thread_zip_process }////////////////////////////////////////////////////////////////////声明一个线程Process,线程名为:zip_process//线程说明文字:“ZIP process”src/ZIP_Router.cPROCESS(zip_process, “ZIP process”);//定义一个数组存放程序中自动运行的线程并将zip_process添加到数组中//contiki初始化完成后将自动运行该线程src/ZIP_Router.cAUTOSTART_PROCESSES(zip_process);执行由AUTOSTART_PROCESSES(zip_process);定义的多个自动运行的线程autostart_start(autostart_processes);//线程定义,参数分别为:参数名,事件ev,事件附带的数据datasrc/ZIP_Router.cPROCESS_THREAD(zip_process, ev, data){//所有线程的实现都是以PROCESS_BEGIN()开始PROCESS_BEGIN()…//所有线程的实现都是以 PROCESS_END()结束PROCESS_END()}两个都是阻塞当前process线程直到一个事件发生不过后者要求 c必须为真。PROCESS_WAIT_EVENT()PROCESS_WAIT_EVENT_UNTIL©//////////////////////////////////////////////发送一个事件给给另一个线程post一个同步事件到一个或者多个processs ,处理此事件的操作将会在目标process被内核调度的时候被处理。一个事件可以被广播到所有的process此时所有的process都会被调度去处理这个事件。参数p ,可以是目标process 或者PROCESS_BROADCASE。int process_poststruct process *p,process_event_t ev,process_data_t data)同步是做程序必须等待该事件完成后才能继续执行下一行代码void process_post_synch(struct process *p, process_event_t ev, process_data_t data)异步是指程序不必等待该事件完成就能执行下一行代码且事件具体完成的时间是事先无法预料的函数 process_post 用于向线程投递一个异步事件int process_post(struct process *p, process_event_t ev, process_data_t data)int process_post(struct process *p, process_event_t ev, process_data_t data){static process_num_events_t snum;if(nevents PROCESS_CONF_NUMEVENTS) {// 事件的缓冲队列满了返回一个错误码给调用线程return PROCESS_ERR_FULL;}// fevent 是事件缓冲队列中缓存的第一个元素的索引// nevents 是事件缓存队列中已缓存的元素的个数// fevent nevents 就表示缓存队列中已缓存数据(最后一个元素)的下一个索引// 也就是用来存储此次事件控制结构的索引// 对 PROCESS_CONF_NUMEVENTS 进行求模运算是因为缓冲队列是环形的snum (process_num_events_t)(fevent nevents) % PROCESS_CONF_NUMEVENTS;// 将事件的控制结构信息保存到该索引所对应的内存空间里面。events[snum].ev ev;events[snum].data data;events[snum].p p;// 缓冲队列中元素已缓冲元素个数递增 1nevents;return PROCESS_ERR_OK;}////////////////////////////////////////////让时间事件感知时钟的变化 一般在时钟中断里面当tick增加的时候将本线程(事件定时器线程)设置为高优先级调度器会优先再次调用本线程遍历链表void etimer_request_poll(void)查看是否有有过期的时间事件void etimer_pendingvoid)得到下一个时间事件流逝的时间clock_timer_t etimer_next_expiration_time(void)启动一个processvoid process_start(struct process *p ,char *arg)终止一个processvoid process_exit(struct process *p)当一个process poll一个事件时会执行这个操作p,但是必须在PROCESS_BEGIN()之前声明。PROCESS_POLLHANDLER§;当一个process退出的时候会执行这个操作p但是必须在PROCESS_BEGIN()之前声明。PROCESS_EXITHANDLER§;分配一个全局的事件号。contiki中事件号大于128的是全局事件号可以从一个process中post到另一个process中process_event_t process_alloc_event(void)运行一次系统 --调用poll 函数 并且处理一个事件这个函数应该被main函数反复的调用来真正的运行contiki 操作系统它会调用需要轮询的服务函数然后处理一个事件这个函数将会返回在事件队列里面等待处理的事件个数以便当队列里面没有等待事件时可以让cpu休眠。int process_run(void)阻塞当前进程也就是当前进程放弃running,放弃cpu使用权PROCESS_YIELD();阻塞当前运行的process 直到condition为真PROCESS_YIELD_UNTIL©Contiki 维护了一个全局的线程链表 struct process *process_list所有被启动的线程都将加入到这个线程链表中全局变量 struct process *process_current它始终指向当前正在被调度的线程Contiki 中的线程有三种状态PROCESS_STATE_NONE无效态。表示一个线程没有被启动或者已经被退出。处于这种状态的线程不在线程链表中。PROCESS_STATE_CALLED执行态。表示一个线程正在被调度即正在执行线程的执行实体(入口函数)。处于这种状态的线程在线程链表中。PROCESS_STATE_RUNNING可执行态(就绪态)。表示线程处于可执行态即线程位于线程链表中等待某一个时刻被调度、执行在 Contiki 中线程有两个优先级普通优先级。线程结构体 struct process 中的成员 neeedspoll 为 0 的线程。高优先级。线程结构体 struct process 中的成员 neeedspoll 为 1 的线程。/////////////////////////////////////////////通过 process_poll 函数可以将一个线程设置为高优先级的线程。故而此线程会被轮询poll 的本意是轮询但是在 Contiki 中所有 needspoll 被设为 1 的线程会比一般线程先被轮询所以直接将 process_poll 叫做设置线程的优先级这样更容易理解。void process_pollstruct process *p)全局变量 poll_requested用来表示当前所有线程中是否有高优先级的线程。poll_request 为 1 表示有高优先级线程否则无高优先级线程poll 的本意是轮询但是在 Contiki 中所有 needspoll 被设为 1 的线程会比一般线程先被轮询所以直接将 process_poll 叫做设置线程的优先级这样更容易理解。contiki/core/sys/process.cvoidprocess_poll(struct process *p){if(p ! NULL) {if(p-state PROCESS_STATE_RUNNING || p-state PROCESS_STATE_CALLED) {// 现将线程的 needspoll 成员置为 1p-needspoll 1;// 再将全局变量 poll_requested 置为 1以告诉线程的调度器// 现在有线程处于高优先级状态。然后调度器在下次调度线程时会// 优先调度高优先级的线程poll_requested 1;}}}///////////////////////////////////////函数 process_start 用于启动一个已经创建的线程contiki/core/sys/process.cvoidprocess_start(struct process *p, process_data_t data){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; // 初始化线程的上下文。详细信息请参考 线程切换 一节。 PT_INIT(p-pt); PRINTF(process: starting %s\n, PROCESS_NAME_STRING(p)); // 先线程投递一个同步事件 PROCESS_EVENT_INIT // 关于投递事件请参考 初始事件 一节 process_post_synch(p, PROCESS_EVENT_INIT, data);}//////////////////////////////////函数 call_process 才会真正去执行一个线程contiki/core/sys/process.cstatic voidcall_process(struct process *p, process_event_t ev, process_data_t data){int ret;#if DEBUGif(p-state PROCESS_STATE_CALLED) {printf(“process: process ‘%s’ called again with event %d\n”, PROCESS_NAME_STRING§, ev);}#endif /* DEBUG */// 如果线程是处于可执行态且线程的入口函数不为 NULL则执行该线程的执行实体(入口函数)。if((p-state PROCESS_STATE_RUNNING) p-thread ! NULL) {PRINTF(“process: calling process ‘%s’ with event %d\n”, PROCESS_NAME_STRING§, ev);// 将 process_current 指向即将被调度的线程process_current p;// 设置线程的状态为“正在被执行态”p-state PROCESS_STATE_CALLED;// 调用该线程的执行实体即切换到该线程去执行ret p-thread(p-pt, ev, data);// 代码走到这里说明已经执行完 p-thread 这个线程实体了。在 p-thread 这个线程实体内部// 会返回一个返回代码表示该线程执行实体是由于什么原因而返回。关于这个返回状态更详细的信// 息请参考 线程切换 一节。if(ret PT_EXITED || ret PT_ENDED || ev PROCESS_EVENT_EXIT) {// 根据线程入口函数的返回值判断是否要将该线程从线程链表中删除// exit_process() 会对线程做一些清理工作然后将其从线程链表中删除exit_process(p, p);} else {// 将线程的状态由“正在被执行态”切换回“等待被执行态”p-state PROCESS_STATE_RUNNING;}}}///////////////////////////////Contiki 中线程的调度策略体现在 process_run 这个函数中contiki/core/sys/process.cintprocess_run(void){// 如果有线程被置为高优先级则先调度这些线程if(poll_requested) {do_poll();}// 再调度一个普通线程do_event();// nevents 表示当前系统还有多少个事件需要处理更多信息请参考 初识事件 一节。return nevents poll_requested;}/////////////////////////////////处理高优先级线程contiki/core/sys/process.cdo_poll 这个函数将线程链表中所有需要高优先级的线程都执行了之后才返回的do_poll:static voiddo_poll(void){struct process *p;poll_requested 0;// 轮询线程链表中的所有线程如果该线程为高优先级的线程则执行该线程for(p process_list; p ! NULL; p p-next) {if(p-needspoll) {p-state PROCESS_STATE_RUNNING;p-needspoll 0;// 调用 call_process 调度/执行该线程call_process(p, PROCESS_EVENT_POLL, NULL);}}}/////////////////////////////////////函数 add_timer() 用于将一个事件定时器添加到事件定时器链表中static void add_timer(struct etimer *timer){struct etimer *t;etimer_request_poll();if(timer-p ! PROCESS_NONE) {// 如果定时器已经在链表中查找到该定时器然后重新绑定线程for(t timerlist; t ! NULL; t t-next) {if(t timer) {timer-p PROCESS_CURRENT();update_time();return;}}}// 代码走到这里说明定时器不在链表中// 为定时器绑定当前线程timer-p PROCESS_CURRENT();// 将定时器加入到链表的表头timer-next timerlist;timerlist timer;update_time();}/////////////////////////////////事件定时器线程只处理两类事件PROCESS_EVENT_EXITED 和 PROCESS_EVENT_POLL。如果接收到投递过来的其它事件直接返回(PROCESS_YIELD() 会保存当前线程的上下文然后退出当前线程)PROCESS_THREAD(etimer_process, ev, data){struct etimer *t, *u;PROCESS_BEGIN();timerlist NULL;while(1) {PROCESS_YIELD();if(ev PROCESS_EVENT_EXITED) { // 有线程将要退出了它向内核中的所有线程都投递了一个 PROCESS_EVENT_EXITED 事件 // 以通知所有的线程(如果需要)做清理工作。 // 当线程退出时当它向所有线程投递 PROCESS_EVENT_EXITED 事件时所绑定的 data // 指向的该线程自己。 // 关于线程退出请参考后续的《线程退出》一节。 struct process *p data; // 指向将要退出的线程。 while(timerlist ! NULL timerlist-p p) { // 先将定时器链表的前面连续的若干个绑定了需要退出线程的定时器从链表中删除 timerlist timerlist-next; } // 再遍历整个链表当查找到有定时器所绑定的线程就是需要退出的那个线程时 // 将该定时器从链表中删除 if(timerlist ! NULL) { t timerlist; while(t-next ! NULL) { if(t-next-p p) { t-next t-next-next; } else t t-next; } } continue; } else if(ev ! PROCESS_EVENT_POLL) { continue; }// 代码走到这里说明本线程接收到的事件为 PROCESS_EVENT_POLL。again:u NULL;// 遍历这个链表查看是否有定时器到期了for(t timerlist; t ! NULL; t t-next) {if(timer_expired(t-timer)) {// 如果有定时器到期了则向该定时器绑定的线程投递一个定时器事件 PROCESS_EVENT_TIMER。if(process_post(t-p, PROCESS_EVENT_TIMER, t) PROCESS_ERR_OK) {// 如果定时器事件投递成功// 先解除定时器绑定的线程t-p PROCESS_NONE;// 再将该定时器从链表中删除if(u ! NULL) {u-next t-next;} else {timerlist t-next;}t-next NULL;update_time();goto again;} else {// 如果定时器事件投递不成功将本线程(事件定时器线程)设置为高优先级// 调度器会优先再次调用本线程遍历链表。etimer_request_poll();}}// u 表示在链表中到期定时器的前一个定时器u t;}}PROCESS_END();}//////////////////////////////////////////Contiki 中的调度器使用 do_event 函数处理缓冲在事件缓冲队列中的事件函数 do_event 只从事件的缓冲队列中取出了一个事件然后投递给对于的线程即 do_event 每次只处理一个事件static void do_event(void){static process_event_t ev;static process_data_t data;static struct process *receiver;static struct process *p;// 如果缓冲队列中缓冲了数据处理之if(nevents 0) {// 取出缓冲队列中的第一个数据即索引 fevent 处存放的数据ev events[fevent].ev;data events[fevent].data;receiver events[fevent].p;// 取出数据后fevent 1 // 对 PROCESS_CONF_NUMEVENTS 进行求模运算是因为缓冲队列是环形的 fevent (fevent 1) % PROCESS_CONF_NUMEVENTS; // 取出数据后缓冲队列缓冲的总元素个数递减 1 --nevents; // Contiki 中定义了一个特殊的线程叫做广播线程即 PROCESS_BROADCAST // 当向该线程投递事件时实际上相当于给所有的线程都投递了一个事件 if(receiver 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) { // 如果该事件是一个初始化事件先修改该线程的状态为可执行态 receiver-state PROCESS_STATE_RUNNING; } // 调用线程 call_process(receiver, ev, data); }}//////////////////////////////////////////////////////