contiki-process_run()
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()的更多相关文章
- 简单的玩玩etimer <contiki学习笔记之九>
好吧,我承认etimer有点小复杂,主要是它似乎和contiki的process搅在一起,到处都在call_process.那就先搜搜contiki下的etimer的example看看,然后再试着写一 ...
- contiki-main.c 一 打印观察 <contiki学习之五>
说明: 本文依赖于 contiki/platform/native/contiki-main.c 文件. 在项目工程目录下的hello-world.c 文件里面,有许多的宏,但没有最关键的main() ...
- Contiki Process概述
本文涉及到的Protothread机制知识,在http://www.cnblogs.com/songdechiu/p/5793717.html 一.进程类型 进程类型主要有协同式(cooperativ ...
- Contiki学习笔记 第一个程序:Hello World
想来想去,还是得先写一个程序,找下感觉,增强一下自信心,那就国际惯例Hello World吧.先到这个网址下一个Instant Contiki 2.7.之所以没用3.0的,是因为有些问题,我源码是下的 ...
- contiki在keil下的stm32平台移植
参考博客: http://www.aiuxian.com/article/p-705047.html http://blog.csdn.net/u013232419/article/details/4 ...
- 专为物联网开发的开源操作系统Contiki(转)
专为物联网开发的开源操作系统Contiki(转) (2012-04-19 15:31:09) 原文网址:http://blog.sina.com.cn/s/blog_6de000c201010z7n ...
- 简单的玩玩etimer <contiki学习笔记之九 补充>
这幅图片是对前面 <<contiki学习笔记之九>> 的一个补充说明. 简单的玩玩etimer <contiki学习笔记之九> 或许,自己正在掀开contiki ...
- PROCESS_YIELD()宏使用及过程分析<contiki学习笔记之八>
好吧,昨晚上研究了switch()的底层实现原理--发现它并不是一般C语言教科书上那样所言,当然,这对于本身就非常熟悉汇编的同学来说,是小菜一碟.世界上,很多事情是巧合与必然的结合体,没有无缘无故的爱 ...
- PROCESS_YIELD()宏和C语言的switch语句< contiki学习笔记之七>
写在前面: 按照main()函数的代码一行一行的分析,该是看到了 etimer_process 这个位置.但是etimer_process实现里的一个宏 PROCESS_YIELD()引出了很多故事 ...
- contiki-main.c 中的process系列函数学习笔记 <contiki学习笔记之六>
说明:本文依然依赖于 contiki/platform/native/contiki-main.c 文件. ---------------------------------------------- ...
随机推荐
- [Asp.net]Uploadify上传大文件,Http error 404 解决方案
引言 之前使用Uploadify做了一个上传图片并预览的功能,今天在项目中,要使用该插件上传大文件.之前弄过上传图片的demo,就使用该demo进行测试.可以查看我的这篇文章:[Asp.net]Upl ...
- WebIM 聊天 Demo
最近 2 个月用业余时间写了一个 IM ,动手之前想了很多,包括前期设计.语言.数据库等,经过了一番思想斗争,最终前台用 Vue.js 展示,Server 使用 node ,数据库使用 MongoDB ...
- Android应用开发中出现appcompat-v7错误
博客Melon麦东=原创记录 经常很多朋友在Android应用开发过程中,遇到创建的好的Android工程,出现appcompat-v7错误,这因为Android从5.0开始,引入了此项目库,解决方案 ...
- .net之工作流工程展示及代码分享(预告)
最近在帮公司做一个工作流程序模块,要求是可以嵌入到各种现有的程序中去.我想把自己制作的思路和过程同大家分享. 先上一张结构图: 由于该项目我一个人做,所以系统结构不能太复杂. 用到的技术主要有:DDD ...
- 快速排序算法 java 实现
快速排序算法 java 实现 快速排序算法Java实现 白话经典算法系列之六 快速排序 快速搞定 各种排序算法的分析及java实现 算法概念 快速排序是C.R.A.Hoare于1962年提出的一种划分 ...
- http 协议集合,超级简单
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Web; ...
- [Effective JavaScript 笔记]第45条:使用hasOwnProperty方法以避免原型污染
之前的43条,44条讨论了属性的枚举,但都没有彻底地解决属性查找中原型污染的问题.看下面关于字典的一些操作 'zhangsan' in dict; dict.zhangsan; dict.zhangs ...
- iOS 多语言的切换
一.添加应用程序需要支持的国际语言 二.新建一个Localizable.strings文件,作为多语言对应的词典,存储多种语言 三.在Localizable.strings的对应文件以键值对的形式配置 ...
- Qt qml listview 列表视图控件(下拉刷新、上拉分页、滚动轴)
Qt qml listview下拉刷新和上拉分页主要根据contentY来判断.但要加上顶部下拉指示器.滚动条,并封装成可简单调用的组件,着实花了我不少精力:) [先看效果] [功能] 下拉刷新 ...
- C语言 04 进制
%d 或者%i 十进制 %c 输出字符 %p 输出地址 %f 输出小数 %o 八进制 %x 十六进制 一个int类型变量占4字节,占32bit(位) 例子:十进制 int=12 转二进制 0000 ...