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

  函数体thread以PROCESS_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

时间: 2024-10-25 20:04:42

contiki-process_run()的相关文章

Contiki 学习笔记:????process_run 解析

process_run用于处理系统所有needspoll标记为1的进程及处理事件队列的下一个事件.本文深入原码,详细分析,也包括do_poll和do_event函数. 一.运行process_run int main() { dbg_setup_uart(); usart_puts("Initialising\n"); clock_init(); process_init(); process_start(&etimer_process, NULL); autostart_st

简单的玩玩etimer <contiki学习笔记之九>

好吧,我承认etimer有点小复杂,主要是它似乎和contiki的process搅在一起,到处都在call_process.那就先搜搜contiki下的etimer的example看看,然后再试着写一个demo玩玩. 在写demo之前,先说说自己是怎么找到etimer 的example的文件的. 在core/sys/etimer.h 文件中,在描述etimer的数据结构的时候,作者显示的指出,如果要使用etimer,就必须先使用 etimer_set()这个函数进行一些工作,如图: 是的,数据结

Contiki Process概述

本文涉及到的Protothread机制知识,在http://www.cnblogs.com/songdechiu/p/5793717.html 进程类型 进程类型主要有协同式(cooperative)和抢占式(preemptive)两种. 协同式进程,要等其他进程运行完进程实体函数(进程不一定运行完,这个时候有可能是阻塞,总之只要执行到return语句,具体看protothread机制),然后才能开始运行. 抢占式进程,会优先运行,当有抢占式进程需要执行时,协同式进程将会被挂起,直到抢占式进程实

contiki-main.c 一 打印观察 <contiki学习之五>

说明: 本文依赖于 contiki/platform/native/contiki-main.c 文件. 在项目工程目录下的hello-world.c 文件里面,有许多的宏,但没有最关键的main()函数出现,也无法知道这个 文件里的 "hello world"什么时候打印.那么只能根据makefile文件的框架和提示,进入到 contiki/platform/ 目录下一探明白.由于前面选择的是  contiki/examples/hello-world/  目录下的工程进行学习,那么

Contiki 2.7 Makefile 文件(三)

2.第二部分 这里的usage,targets,savetarget,savedefines都是伪目标. 和all不同,这些伪目标不会被执行,除非显式指定这些目标. 这里有两个目标savetarget,savedefines前边我们提过. 通过命令 make TARGET=esb savetarget可以保存默认的TARGET到Makefile.target中. 通过命令 make TARGET=esb DEFINES=MYTRACE,MYVALUE=4711 savedefines 可以保存默

Contiki 2.7 Makefile 文件(二)

二.Makefile.include 1.第一部分 (1) ifndef CONTIKI ${error CONTIKI not defined! You must specify where Contiki resides} endif 含义: 如果没有定义CONTIKI变量,make停止运行. 并产生一个致命的错误信息,CONTIKI not  defined! You must specify where Contiki resides. hello-world这个例子在主控Makefil

Contiki 2.7 Makefile 文件(一)

一.主控Makefile 这里以hello-world例子为主线,从其工程Makefile开始,解析整个build过程. (1)CONTIKI_PROJECT = hello-world 定义变量CONTIKI_PROJECT为 hello-world (2)all:  $(CONTIKI_PROJECT) all是第一个目标,也就是默认目标,其为伪目标,依赖于CONTIKI_PROJECT变量定义的文件. 由于默认目标的特性是,总是被执行(除非显式定义了目标),但由于all是伪目标,只是一个标

Contiki 源码风格

/** * \defgroup coding-style Coding style * * This is how a Doxygen module is documented - start with a \defgroup * Doxygen keyword at the beginning of the file to define a module, * and use the \addtogroup Doxygen keyword in all other files that * b

Contiki Rtimer 模块

一.rtimer概述 The Contiki rtimer library provides scheduling and execution of real-time tasks (with predictable execution times) 数据结构: struct rtimer { rtimer_clock_t time; rtimer_callback_t func; void *ptr; }; typedef void (* rtimer_callback_t)(struct r

cc2530 makefile简略分析 <contiki学习之三>

前面将contiki的makefile框架都理了下,这篇就以cc2530为收篇吧,也即makefile分析就该到此为止了. contiki/examples/cc2530dk 打开Makefile如下图: 第一行的 "CONTIKI_PROJECT"变量依赖于该目录下的  blink-hello.c  hello-world.c  sensors-demo.c  timer-test.c 4个C源文件 第五行的"CONTIKI"变量被定义成 contiki/ 路径,