contiki-process_run()

Posted 我为自己代言

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了contiki-process_run()相关的知识,希望对你有一定的参考价值。

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的。

  函数体threadPROCESS_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

以上是关于contiki-process_run()的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段——CSS选择器

谷歌浏览器调试jsp 引入代码片段,如何调试代码片段中的js

片段和活动之间的核心区别是啥?哪些代码可以写成片段?

VSCode自定义代码片段——.vue文件的模板

VSCode自定义代码片段6——CSS选择器

VSCode自定义代码片段——声明函数