本文深入源码,详细分析Contiki启动一个进程的过程。先是总结了process_start()
都做了些什么事,进而跟踪代码进行详细分析。
引言
process_start
函数用于启动一个进程,将进程加入进程链表(事先验证参数,确保进程不在进程链表中),初始化进程(将进程状态设为运行状态及lc
设为0)。给进程传递一个PROCESS_EVENT_INIT
事件,让其开始执行(事先参数验证,确保进程已被设为运行态并且进程的函数指针thread
不为空),事实上是执行进程结构体中的thread
函数指针所指的函数,而这恰恰是PROCESS_THREAD(name, ev, data)
函数的实现。判断执行结果,如果返回值表示退出、结尾或者遇到PROCESS_EVENT_EXIT
,进程退出,否则进程被挂起,等待事件。
理解系统一个好的方法是阅读源码,分享Linus的一句话,接下来跟踪代码详细分析Contiki系统是如何启动一个进程的。
Read the Fucking Source Code --Linus Torvalds
整个调用过程如下:
process_start——>process_post_synch——>call_process——>exit_process
1. process_start函数
将进程加入进程链表(事先验证参数,确保进程不在进程链表中),初始化进程(将进程状态设为运行状态及lc设为0)。给进程传递一个PROCESS_EVENT_INIT
事件,让其开始执行。
/*******启动一个进程********/
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;
PT_INIT(&p->pt); //将p->pt->lc设为0,使得进程从case 0开始执行
PRINTF("process: starting '%s'\n", PROCESS_NAME_STRING(p));
//给进程传递一个PROCESS_EVENT_INIT事件,让其开始执行
process_post_synch(p, PROCESS_EVENT_INIT, (process_data_t)arg);
}
2. process_post_synch函数
process_post_synch()
直接调用call_process()
,期间需要保存process_current
,这是因为当调用call_process
执行这个进程p
时,process_current
就会指向当前进程p
,而进程p
可能会退出或者被挂起等待一个事件。
//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;
{
struct process *caller = process_current; //相当于PUSH,保存现场process_current
call_process(p, ev, data);
process_current = caller; //相当于POP,恢复现场process_current
}
3. call_process函数
如果进程process
的状态为PROCESS_STATE_RUNNING
,并且进程中的thread
函数指针(相当于该进程的主函数)不为空的话,就执行该进程。如果返回值表示退出、结尾或者遇到PROCESS_EVENT_EXIT
,进程退出,否则进程被挂起,等待事件。
//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 */
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 = 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; //进程挂起等待事件
}
}
}
PT四种状态
#define PT_WAITING 0 /*被阻塞,等待事件发生*/
#define PT_YIELDED 1 /*主动让出*/
#define PT_EXITED 2 /*退出*/
#define PT_ENDED 3 /*结束*/
进程状态
#define PROCESS_STATE_NONE 0 /*类似于Linux系统的僵尸状态,进程已退出,只是还没从进程链表删除*/
#define PROCESS_STATE_RUNNING 1 /*进程就绪状态*/
#define PROCESS_STATE_CALLED 2 /*进程被调用,运行状态*/
4. exit_process函数
先进行参数验证,确保进程在进程链表中并且不是PROCESS_STATE_NONE
状态,向所有进程发一个同步事件PROCESS_EVENT_EXITED
,通知他们将要退出,让与该进程相关的进程进行相应处理。
如果一个程序要退出的话,就会给etimer_process
进程发送一个PROCESS_EVENT_EXITED
事件,那么收到这个事件之后,etimer_process
就会查找timerlist
看看哪个timer
是与这个进程相关的,就把它从timerlist
中清除。
//struct process *p 指要退出的进程
//struct process *fromprocess 指当前的进程 ?
//exit_process(p, p)
static void exit_process(struct process *p, struct process *fromprocess)
{
register struct process *q;
struct process *old_current = process_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->thread(&p->pt, PROCESS_EVENT_EXIT, NULL); //给进程p传递一个PROCESS_EVENT_EXIT事件,通常用于退出死循环(如PROCESS_WAIT_EVENT函数),从而使其从主体函数退出
}
}
/*将进程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;
}