Contiki内核结构
嵌入式系统可以看作是一个运行着死循环主函数系统,Contiki内核是基于事件驱动的,系统运行可以视为不断处理事件的过程。Contiki整个运行是通过事件触发完成,一个事件绑定相应的进程。当事件被触发,系统把执行权交给事件所绑定的进程,contiki系统运行原理示意图如下所示:
contiki系统的process
contiki系统中process的结构体如下所示:
struct process {
struct process *next;
#if PROCESS_CONF_NO_PROCESS_NAMES /* More CODE space savings by turning off process names */
#define PROCESS_NAME_STRING(process) ""
#else
const char *name;
#define PROCESS_NAME_STRING(process) (process)->name
#endif
PT_THREAD((* thread)(struct pt *, process_event_t, process_data_t));
struct pt pt;
unsigned char state, needspoll;
};
next:contiki事件以单链表形式存储,此处指向当前事件的下一个事件地址,contiki定义了一个process_list的全局变量来标记事件的表头;
PROCESS_CONF_NO_PROCESS_NAMES:进程名称,为0时进程名称为空字符串;
pt:记录当前中断状态,其实是一个void*的指针,用来保存被中断的行数;
state:标记进程的状态,共有三种状态,分别为:PROCESS_STATE_NONE, PROCESS_STATE_RUNNING, PROCESS_STATE_CALLED;
needspoll:进程优先级,取值为0 或者1,为1表示优先级更高,需要将该进程插入到其他普通进程前;
PT_THREAD:进程处理函数,此处为一个函数的指针;struct pt *用来标记进程状态;process_event_t为进程中事件的名称,原型为unsigned char;process_data_t为进程传递的参数,原型为void *类型;
进程调度
进程创建
创建进程实际上是定义一个进程控制块和进程执行体的函数;PROCESS宏定义即可用来定义一个进程控制块和声明一个进程执行体函数,name为进程控制块,PROCESS_THREAD为进程执行体函数。
#define PROCESS(name, strname) \
PROCESS_THREAD(name, ev, data); \
struct process name = { NULL, strname, \
process_thread_##name }
需要注意的是,PROCESS宏定义只是对进程的执行函数做了声明,但是并没有定义,所以还需要对PROCESS_THREAD函数进行定义;
PROCESS_THREAD(hello_world_process, ev, data)
{
PROCESS_BEGIN();
printf("Hello World!\n");
PROCESS_END();
}
进程创建完成之后,通过process_start函数将新创建的进程加入到进程执行链表里,process_start的核心代码如下所示;
/* Put on the procs list.*/
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));
/* Post a synchronous initialization event to the process. */
process_post_synch(p, PROCESS_EVENT_INIT, data);
process_post_synch为进程同步函数,内部直接调用call_process函数,此处将进程状态置为PROCESS_STATE_RUNNING,所以第一次将新创建的进程加入到进程链表的时候,会执行一遍进程的thread函数,然后通过exit_process函数将state置为PROCESS_STATE_NONE,exit_process函数核心代码如下所示;
if(process_is_running(p)) {
/* Process was running */
p->state = PROCESS_STATE_NONE;
/*
* Post a synchronous event to all processes to inform them that
* this process is about to exit. This will allow services to
* deallocate state associated with this process.
*/
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);
}
}
if(p == process_list) { //如果当前进程p为process_list的头节点,则将process_list指向它的next;
process_list = process_list->next;
} else { //如果当前进程p不为process_list的头节点,则将p的上一个节点的next指向p的next,即将p节点从链表中去除掉;
for(q = process_list; q != NULL; q = q->next) {
if(q->next == p) {
q->next = p->next;
break;
...
和process_post_synch对应的为process_post,该函数为进程异步函数,调用process_post函数处理事件时,事件并未立即处理,而是被加入到事件等待处理队列nevents里,等待下一次process_run的时候,通过do_event函数来执行。
进程加入到进程执行链表之后,通过process_run函数来进行进程的轮询调用,process_run核心代码如下所示;即先执行优先级比较高的进程,再执行普通进程。
if(poll_requested) {
do_poll();
}
/* Process one event from the queue */
do_event();
进程优先级
contiki只有两种类型的进程调度优先级,用process中的needspoll标记,默认为0,即普通优先级;可以在创建该进程时设置其优先级;在进程运行时会优先检查高优先级的进程;process_run函数原型如下:
int
process_run(void)
{
/* Process poll events. */
if(poll_requested) {
do_poll();
}
/* Process one event from the queue */
do_event();
return nevents + poll_requested;
}
poll_requested:全局变量,是否有高优先级事件的标志位,初始化为0,在函数process_poll里进行置位;
do_poll:函数原型如下所示,遍历整个进程链表,查看进程优先级,如果进程的needspoll标志位为1,则优先执行该进程,将其状态标记为PROCESS_STATE_RUNNING,然后调用call_process执行该进程;
/* 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);
}
call_process函数核心代码如下:
if((p->state & PROCESS_STATE_RUNNING) &&
p->thread != NULL) {//如果当前进程的状态是PROCESS_STATE_RUNNING模式,并且thread函数不为NULL
PRINTF("process: calling process '%s' with event %d\n", PROCESS_NAME_STRING(p), ev);
process_current = p;
p->state = PROCESS_STATE_CALLED;//将进程的状态置为PROCESS_STATE_CALLED
ret = p->thread(&p->pt, ev, data);//运行进程函数thread,返回值为ret
if(ret == PT_EXITED ||
ret == PT_ENDED ||
ev == PROCESS_EVENT_EXIT) {
exit_process(p, p);//如果该进程被正常执行,则退出该进程
} else {
p->state = PROCESS_STATE_RUNNING;
}
以上即为contiki系统process管理的整个过程,主要分为进程创建和进程管理两大部分内容,进程创建其实就是对进程控制块和进程执行函数进行注册和声明;进程管理其实就是对进程各个状态的维护和处理。
contiki系统的事件
事件也是contiki系统的重要组成部分,在contiki系统中,当有事件传递给进程时,就会新建一个事件加入事件队列,并绑定该进程;一个进程可以对应多个事件,一个事件也可以通知给所有的进程;事件的数据结构如下所示;
struct event_data {
process_event_t ev; //unsigned char
process_data_t data; //void *
struct process *p;
};
ev:标识所产生事件ID,0x80-0x8F为系统事件ID,其余的为用户可自定义的事件ID;
data:保存事件产生时获得的相关信息,即事件产生后可以给进程传递的数据;
p:指向监听该事件的进程;
static struct event_data events[PROCESS_CONF_NUMEVENTS];
通过PROCESS_CONF_NUMEVENTS宏定义来确定事件队列的大小;将需要执行的事件加入到events队列中,在系统调用process_run的时候,通过调用do_event函数来执行events队列中的事件;do_event核心代码如下所示;
if(nevents > 0) {
/* There are events that we should deliver. */
ev = events[fevent].ev;
data = events[fevent].data;
receiver = events[fevent].p;
/* Since we have seen the new event, we move pointer upwards
and decrease the number of events. */
fevent = (fevent + 1) % PROCESS_CONF_NUMEVENTS;
--nevents;
/* If this is a broadcast event, we deliver it to all events, in
order of their priority. */
if(receiver == PROCESS_BROADCAST) {
for(p = process_list; p != NULL; p = p->next) {
/* If we have been requested to poll a process, we do this in
between processing the broadcast event. */
if(poll_requested) {
do_poll();
}
call_process(p, ev, data);
}
} else {
/* This is not a broadcast event, so we deliver it to the
specified process. */
/* If the event was an INIT event, we should also update the
state of the process. */
if(ev == PROCESS_EVENT_INIT) {
receiver->state = PROCESS_STATE_RUNNING;
}
/* Make sure that the process actually is running. */
call_process(receiver, ev, data);
}
}
如果事件队列里有需要执行的事件,则通过call_process函数将该事件的相关参数传递给相应的process进行执行。