contiki--process详解

Contiki内核结构

嵌入式系统可以看作是一个运行着死循环主函数系统,Contiki内核是基于事件驱动的,系统运行可以视为不断处理事件的过程。Contiki整个运行是通过事件触发完成,一个事件绑定相应的进程。当事件被触发,系统把执行权交给事件所绑定的进程,contiki系统运行原理示意图如下所示:

contiki--process详解_第1张图片

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进行执行。

你可能感兴趣的:(contiki--process详解)