process_run()函数位于main函数中
while(1) { do { } while(process_run() > 0); idle_count++; }
找到函数的声明处:
/** * Run the system once - call poll handlers and process one event. * * This function should be called repeatedly from the main() program * to actually run the Contiki system. It calls the necessary poll * handlers, and processes one event. The function returns the number * of events that are waiting in the event queue so that the caller * may choose to put the CPU to sleep when there are no pending * events. * * \return The number of events that are currently waiting in the * event queue. */ int process_run(void);
函数process_run()返回当前在事件队列中等待的事件的数量,当没有即将发生的事件时,调度器会让CPU休眠
函数原型如下:
int process_run(void) { /* Process poll events. */ if(poll_requested) { do_poll(); } /* Process one event from the queue */ do_event(); return nevents + poll_requested; }
主要函数为do_poll()和do_event()。
static void do_poll(void) { struct process *p; poll_requested = 0; /* Call the processes that needs to be polled. */ for(p = process_list; p != NULL; p = p->next) {//遍历进程链表 if(p->needspoll) { p->state = PROCESS_STATE_RUNNING;//设置进程状态 p->needspoll = 0; call_process(p, PROCESS_EVENT_POLL, NULL);//将进程投入运行 } } }
以上是进程的总体调度,具体到单个进程,成员变量state标示着进程的状态,共有三个状态PROCESS_STATE_RUNNING 、 PROCESS_STATE_CALLED 、 PROCESS_STATE_NONE。Contiki进程状态转换如下图:
创建进程(还未投入运行)以及进程退出(但此时还没从进程链表删除),进程状态都为PROCESS_STATE_NONE。通过进程启动函数process_start()将新创建的进程投入运行队列(但未必有执行权),真正获得执行权的进程状态为PROCESS_STATE_CALLED,处在运行队列的进程(包括正在运行和等待运行),可以调用exit_process()退出。
进程运行是由call_process函数实现。流程图如下:
1 static void call_process(struct process *p, process_event_t ev,process_data_t data) 2 { 3 int ret; 4 5 #if DEBUG 6 if(p->state == PROCESS_STATE_CALLED) { 7 printf("process: process ‘%s‘ called again with event %d\n", PROCESS_NAME_STRING(p), ev); 8 } 9 #endif /* DEBUG */ 10 /*进程状态为RUNNING*/ 11 if((p->state & PROCESS_STATE_RUNNING) &&p->thread != NULL) { 12 PRINTF("process: calling process ‘%s‘ with event %d\n", PROCESS_NAME_STRING(p), ev); 13 process_current = p; 14 p->state = PROCESS_STATE_CALLED;//进程状态设为CALLED 15 ret = p->thread(&p->pt, ev, data);//执行函数体thread 16 if(ret == PT_EXITED ||ret == PT_ENDED ||ev == PROCESS_EVENT_EXIT) { 17 exit_process(p, p);//如果进程结束或退出,则退出进程 18 } 19 else {//如果进程没有结束,则将进程状态设为RUNNING 20 p->state = PROCESS_STATE_RUNNING; 21 } 22 } 23 }
call_process首先进行参数验证,即进程处于运行状态(退出尚未删除的进程状态为PROCESS_STATE_NONE)并且进程的函数体不为空,接着将进程状态设为PROCESS_STATE_CALLED,表示该进程拥有执行权。接下来,运行进程函数体,根据返回值判断进程是否结束(主动的)或者退出(被动的),若是调用exit_process,将进程退出,否则,将进程状态设为PROCESS_STATE_RUNNING,继续放在进程链表。
函数体thread返回值类型
#define PT_WAITING 0 #define PT_YIELDED 1 #define PT_EXITED 2 #define PT_ENDED 3
这里追踪一个,如何返回PT_ENDED的。
函数体thread以PROCESS_END()结束,最后调用宏PT_END,其中有返回PT_ENDED,即函数体thread的返回值。
#define PROCESS_END() PT_END(process_pt) #define PT_END(pt) LC_END((pt)->lc); PT_YIELD_FLAG = 0; \ PT_INIT(pt); return PT_ENDED; }
进程初始化
系统启动后需要先将进程初始化,通常在主函数调用,进程初始化主要完成事件队列和进程链表初始化。将进程链表头指向为空,当前进程也设为空。
process_init()源代码如下:
void process_init(void) { /*初始化事件队列*/ lastevent = PROCESS_EVENT_MAX; nevents = fevent = 0; #if PROCESS_CONF_STATS process_maxevents = 0; #endif /* PROCESS_CONF_STATS */ /*初始化进程链表*/ process_current = process_list = NULL; }
创建进程
创建进程实际上是定义一个进程控制块和定义进程执行体的函数。宏PROCESS的功能包括定义一个结构体,声明进程执行体函数。
进程名以Hello world为例。
PROCESS(hello_world_process, "Hello world"); /*PROCESS宏展开*/ #define PROCESS(name, strname) \ PROCESS_THREAD(name, ev, data); struct process name = { NULL, strname, process_thread_##name } /*PROCESS_THREAD宏展开*/ #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 PT_THREAD(name_args) char name_args /*将参数带入,PT_THREAD宏最后展开结果*/ static char process_thread_hello_world_process(struct pt *process_pt, process_event_t ev, process_data_t data); struct process hello_world_process={NULL."Hello world",process_thread_hello_world_process};
可见,PROCESS宏实际上声明一个函数并定义一个进程控制块,新创建的进程next指针指向空,进程名称“Hello world”,进程执行体函数指针为process_thread_hello_world_process,保存行数的pt为0,状态为0(即PROCESS_STATE_NONE),优先级标记位needspoll也为0(即普通优先级)。
PROCESS定义了结构体并声明了函数,还需要实现该函数,通过宏PROCESS_THREAD实现。值得注意的是,尽管PROCESS宏展开包含了宏PROCESS_THREAD,用于声明函数,而这里是定义函数,区别在于前者宏展开后面加了个分号。定义函数框架代码如下:
PROCESS_THREAD(hello_world_process, ev, data) //static char process_thread_hello_world_process(struct pt *process_pt, process_event_t ev, process_data_t data) { PROCESS_BEGIN(); //函数开头必须有 /***代码放在这***/ PROCESS_END(); //函数末尾必须有 }
欲实现的代码必须放在宏PROCESS_BEGIN()和PROCESS_END ()之间,这是因为这两个宏用于辅助保存断点信息(即行数),宏PROCESS_BEGIN()包含switch(process_pt->lc)语句,这样被中断的进程再次获利执行便可通过switch语句跳转到相应的case,即被中断的行。
启动进程
函数process_start()用于启动一个进程,首先进行参数验证,即判断该进程是否已经在进程链表中,而后将进程加到链表,给该进程发一个初始化事件PROCESS_EVENT_INIT。函数process_start流程图如下:
1 void 2 process_start(struct process *p, const char *arg) 3 { 4 struct process *q; 5 6 /* First make sure that we don‘t try to start a process that is 7 already running. */ 8 /*判断该进程是否在进程链表中*/ 9 for(q = process_list; q != p && q != NULL; q = q->next); 10 11 /* If we found the process on the process list, we bail out. */ 12 if(q == p) { 13 return;//进程在链表中,则退出 14 } 15 //不在链表中,将进程放入进程链表头部 16 /* Put on the procs list.*/ 17 p->next = process_list; 18 process_list = p; 19 p->state = PROCESS_STATE_RUNNING; 20 PT_INIT(&p->pt); 21 22 PRINTF("process: starting ‘%s‘\n", PROCESS_NAME_STRING(p)); 23 24 /* Post a synchronous initialization event to the process. */ 25 /*给该进程发送一个初始化事件PROCESS_EVENT_INIT*/ 26 process_post_synch(p, PROCESS_EVENT_INIT, (process_data_t)arg); 27 }
process_start()将进程状态设为PROCESS_STATE_RUNNING,并调用PT_INIT宏将保存断点的变量设为0(即行数设为0)。调用process_post_synch给进程触发一个同步事件,事件为PROCESS_EVENT_INIT。考虑到进程运行过程中可能被中断,在进程运行前将当前进程指针保存起来,执行完再恢复。
进程退出
进程运行完或者收到退出的事件都会导致进程退出。根据Contiki编程规划,进程函数体最后一条语句是PROCESS_END(),该宏包含语句return PT_ENDED,表示进程运行完毕。系统处理事件时(事件绑定进程,事实上执行进程函数体),倘若该进程恰好收到退出事件,thread便返回PT_EXITED,进程被动退出。还有就是给该进程传递退出事件PROCESS_EVENT_EXIT也会导致进程退出。进程退出函数exit_process()流程图如下:
1 static void 2 exit_process(struct process *p, struct process *fromprocess) 3 { 4 register struct process *q; 5 struct process *old_current = process_current; 6 7 PRINTF("process: exit_process ‘%s‘\n", PROCESS_NAME_STRING(p)); 8 9 /* Make sure the process is in the process list before we try to 10 exit it. */ 11 /*退出该进程之前,确保该进程p处于进程链表中*/ 12 for(q = process_list; q != p && q != NULL; q = q->next); 13 if(q == NULL) { 14 return;//如果不在进程链表中,则直接返回 15 } 16 17 if(process_is_running(p)) { 18 /* Process was running */ 19 p->state = PROCESS_STATE_NONE; 20 21 /* 22 * Post a synchronous event to all processes to inform them that 23 * this process is about to exit. This will allow services to 24 * deallocate state associated with this process. 25 */ 26 for(q = process_list; q != NULL; q = q->next) { 27 if(p != q) { 28 call_process(q, PROCESS_EVENT_EXITED, (process_data_t)p); 29 } 30 } 31 32 if(p->thread != NULL && p != fromprocess) { 33 /* Post the exit event to the process that is about to exit. */ 34 process_current = p; 35 p->thread(&p->pt, PROCESS_EVENT_EXIT, NULL);//执行函数体 36 } 37 } 38 //从链表中删除 39 if(p == process_list) { 40 process_list = process_list->next; 41 } 42 else { 43 for(q = process_list; q != NULL; q = q->next) { 44 if(q->next == p) { 45 q->next = p->next; 46 break; 47 } 48 } 49 } 50 51 process_current = old_current; 52 }
进程退出函数exit_process首先对传进来的进程p进行参数验证,确保该进程在进程链表中并且进程状态为PROCESS_STATE_CALLED/RUNNING(即不能是 NONE),接着将进程状态设为NONE。随后,向进程链表的所有其它进程触发退出事件PROCESS_EVENT_EXITED,此时其他进程依次执行处理该事件,其中很重要的一部分是取消与该进程的关联。进程执行函数体thread进行善后工作,最后将该进程从进程链表删除。
参考大神Jelline的博客:http://jelline.blog.chinaunix.net