美文网首页
contiki--process详解

contiki--process详解

作者: zplodge | 来源:发表于2017-10-08 20:28 被阅读0次

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

    相关文章

      网友评论

          本文标题:contiki--process详解

          本文链接:https://www.haomeiwen.com/subject/gousyxtx.html