process_run()函数位于main函数中

while() {
do
{
}
while(process_run() > );
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 = ;
/* 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 = ;
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函数实现。流程图如下:

 static void call_process(struct process *p, process_event_t ev,process_data_t data)
{
int ret; #if DEBUG
if(p->state == PROCESS_STATE_CALLED) {
printf("process: process '%s' called again with event %d\n", PROCESS_NAME_STRING(p), ev);
}
#endif /* DEBUG */
/*进程状态为RUNNING*/
if((p->state & PROCESS_STATE_RUNNING) &&p->thread != NULL) {
PRINTF("process: calling process '%s' with event %d\n", PROCESS_NAME_STRING(p), ev);
process_current = p;
p->state = PROCESS_STATE_CALLED;//进程状态设为CALLED
ret = p->thread(&p->pt, ev, data);//执行函数体thread
if(ret == PT_EXITED ||ret == PT_ENDED ||ev == PROCESS_EVENT_EXIT) {
exit_process(p, p);//如果进程结束或退出,则退出进程
}
else {//如果进程没有结束,则将进程状态设为RUNNING
p->state = PROCESS_STATE_RUNNING;
}
}
}

  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 = ;
#if PROCESS_CONF_STATS
process_maxevents = ;
#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流程图如下:

 void
process_start(struct process *p, const char *arg)
{
struct process *q; /* First make sure that we don't try to start a process that is
already running. */
/*判断该进程是否在进程链表中*/
for(q = process_list; q != p && q != NULL; q = q->next); /* If we found the process on the process list, we bail out. */
if(q == p) {
return;//进程在链表中,则退出
}
//不在链表中,将进程放入进程链表头部
/* 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_EVENT_INIT*/
process_post_synch(p, PROCESS_EVENT_INIT, (process_data_t)arg);
}

  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()流程图如下:

 static void
exit_process(struct process *p, struct process *fromprocess)
{
register struct process *q;
struct process *old_current = process_current; PRINTF("process: exit_process '%s'\n", PROCESS_NAME_STRING(p)); /* Make sure the process is in the process list before we try to
exit it. */
/*退出该进程之前,确保该进程p处于进程链表中*/
for(q = process_list; q != p && q != NULL; q = q->next);
if(q == NULL) {
return;//如果不在进程链表中,则直接返回
} 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) {
process_list = process_list->next;
}
else {
for(q = process_list; q != NULL; q = q->next) {
if(q->next == p) {
q->next = p->next;
break;
}
}
} process_current = old_current;
}

  进程退出函数exit_process首先对传进来的进程p进行参数验证,确保该进程在进程链表中并且进程状态为PROCESS_STATE_CALLED/RUNNING(即不能是 NONE),接着将进程状态设为NONE。随后,向进程链表的所有其它进程触发退出事件PROCESS_EVENT_EXITED,此时其他进程依次执行处理该事件,其中很重要的一部分是取消与该进程的关联。进程执行函数体thread进行善后工作,最后将该进程从进程链表删除。

参考大神Jelline的博客:http://jelline.blog.chinaunix.net

contiki-process_run()的更多相关文章

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

    好吧,我承认etimer有点小复杂,主要是它似乎和contiki的process搅在一起,到处都在call_process.那就先搜搜contiki下的etimer的example看看,然后再试着写一 ...

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

    说明: 本文依赖于 contiki/platform/native/contiki-main.c 文件. 在项目工程目录下的hello-world.c 文件里面,有许多的宏,但没有最关键的main() ...

  3. Contiki Process概述

    本文涉及到的Protothread机制知识,在http://www.cnblogs.com/songdechiu/p/5793717.html 一.进程类型 进程类型主要有协同式(cooperativ ...

  4. Contiki学习笔记  第一个程序:Hello World

    想来想去,还是得先写一个程序,找下感觉,增强一下自信心,那就国际惯例Hello World吧.先到这个网址下一个Instant Contiki 2.7.之所以没用3.0的,是因为有些问题,我源码是下的 ...

  5. contiki在keil下的stm32平台移植

    参考博客: http://www.aiuxian.com/article/p-705047.html http://blog.csdn.net/u013232419/article/details/4 ...

  6. 专为物联网开发的开源操作系统Contiki(转)

    专为物联网开发的开源操作系统Contiki(转)  (2012-04-19 15:31:09) 原文网址:http://blog.sina.com.cn/s/blog_6de000c201010z7n ...

  7. 简单的玩玩etimer <contiki学习笔记之九 补充>

    这幅图片是对前面  <<contiki学习笔记之九>>  的一个补充说明. 简单的玩玩etimer <contiki学习笔记之九> 或许,自己正在掀开contiki ...

  8. PROCESS_YIELD()宏使用及过程分析<contiki学习笔记之八>

    好吧,昨晚上研究了switch()的底层实现原理--发现它并不是一般C语言教科书上那样所言,当然,这对于本身就非常熟悉汇编的同学来说,是小菜一碟.世界上,很多事情是巧合与必然的结合体,没有无缘无故的爱 ...

  9. PROCESS_YIELD()宏和C语言的switch语句< contiki学习笔记之七>

    写在前面:  按照main()函数的代码一行一行的分析,该是看到了 etimer_process 这个位置.但是etimer_process实现里的一个宏 PROCESS_YIELD()引出了很多故事 ...

  10. contiki-main.c 中的process系列函数学习笔记 <contiki学习笔记之六>

    说明:本文依然依赖于 contiki/platform/native/contiki-main.c 文件. ---------------------------------------------- ...

随机推荐

  1. paper 115:常见的概率分布(matlab作图)

    一.常见的概率分布 表1.1 概率分布分类表 连续随机变量分布 连续统计量分布 离散随机变量分布 分布 分布 二项分布 连续均匀分布 非中心 分布 离散均匀分布 (Gamma)分布 分布 几何分布 指 ...

  2. php读取excel内容

    使用php读取到excel文件中的内容 1.下载PHPExcel类 2.代码: header("Content-type:text/html;charset=utf-8");req ...

  3. 简单封装数据请求(iOS)

    #import <Foundation/Foundation.h> //给block起 别名 //类型 void(^)(BOOL success , id data) //别名是 Comp ...

  4. [ASP.NET MVC] Real-time之HTML5 服务器发送事件(server-sent event)

    最近有时间,打算看看SignalR,顺便了解一下Server Sent Events. Controller 输出的数据格式为:data:[数据]\n\n.输出的数据尝试8000多字符也没问题,具体的 ...

  5. android 使用jdbc1.3.0 操作 sql server

    String connectDB = "jdbc:jtds:sqlserver://172.16.1.15:1433;DatabaseName=YanBu";// 连接字符串换成这 ...

  6. HTML入门教程

    什么是 HTML?     HTML(Hyper Text Markup Language)超文本标记语言,是用来描述网页的一种语言,不是一种编程语言,而是一种标记语言 (markup languag ...

  7. JavaScript,复习总结

    ECMA(European Computer Manufacturers Association)欧洲计算机制造商协会.其制定很多标准:C#语言规范:C++/CLI语言规范:Eiffel语言:CD-R ...

  8. netty 解决TCP粘包与拆包问题(二)

    TCP以流的方式进行数据传输,上层应用协议为了对消息的区分,采用了以下几种方法. 1.消息固定长度 2.第一篇讲的回车换行符形式 3.以特殊字符作为消息结束符的形式 4.通过消息头中定义长度字段来标识 ...

  9. 当call/apply传的第一个参数为null/undefined的时候js函数内执行的上下文对象是什么呢?

    如题:在js中我们都知道call/apply,还有比较少用的bind;传入的第一个参数都是改变函数当前上下文对象;call/apply区别在于传的参数不同,一个是已逗号分隔字符串,一个以数组形式.而b ...

  10. 山东省第七届ACM省赛------Reversed Words

    Reversed Words Time Limit: 2000MS Memory limit: 131072K 题目描述 Some aliens are learning English. They ...