一、HelloWorld例子

#include "contiki.h"#include <stdio.h> /* For printf() */
/*---------------------------------------------------------------------------*/
PROCESS(hello_world_process, "Hello world process");
AUTOSTART_PROCESSES(&hello_world_process);
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(hello_world_process, ev, data)
{PROCESS_BEGIN();printf("Hello, world\n");PROCESS_END();
}

二、PROCESS

#if PROCESS_CONF_NO_PROCESS_NAMES//是否字符串名字?
#define PROCESS(name, strname)                \PROCESS_THREAD(name, ev, data);            \//声明进程执行实体函数struct process name = { NULL,                \process_thread_##name }//定义进程结构体变量name
#else
#define PROCESS(name, strname)                \PROCESS_THREAD(name, ev, data);            \struct process name = { NULL, strname,        \process_thread_##name }
#endif

PROCESS_THREAD(name, ev, data);一步一步展开之后为:

#define PROCESS_THREAD(name, ev, data)                 \
static PT_THREAD(process_thread_##name(struct pt *process_pt,    \process_event_t ev,    \process_data_t data))

PT_THREAD看protothread机制

static char process_thread_hello_world_process(struct pt *process_pt, process_event_t ev, process_data_t data);

这条语句相当于声明一个函数process_thread_hello_world,而这个函数就是进程执行实体函数。在后续的定义进程结构体可以看出。

进程结构体:

struct process {struct process *next;//指向下个进程结构体,在进程链表中使用
#if PROCESS_CONF_NO_PROCESS_NAMES//配置进程字符串名字?
#define PROCESS_NAME_STRING(process) ""//没有,空
#else//有字符串名字const char *name;//定义进程字符串名字
#define PROCESS_NAME_STRING(process) (process)->name//取名字
#endifPT_THREAD((* thread)(struct pt *, process_event_t, process_data_t));//进程执行实体函数struct pt pt;//pt结构体,存储实体函数阻塞时的位置unsigned char state, needspoll;//state是进程状态,needspoll标志进程是否需要优先执行
};

struct process hello_world_process = { NULL, "Hello world process", \                       process_thread_hello_world_process }

后边的的语句定义了一个process变量hello_world_process,并赋初值,为简化这里按有strname来处理。

总之,PROCESS宏定义,有两个功能,声明进程执行实体函数定义进程结构体变量,如下所示:

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", \ process_thread_hello_world_process }

注:这里只需要抓住hello_world_process变量,就抓住了整个进程。

三、AUTOSTART_PROCESSES

全部展开:

AUTOSTART_PROCESSES(&hello_world_process);#if ! CC_NO_VA_ARGS
#if AUTOSTART_ENABLE
#define AUTOSTART_PROCESSES(...)                    \
struct process * const autostart_processes[] = {__VA_ARGS__, NULL}
#else /* AUTOSTART_ENABLE */
#define AUTOSTART_PROCESSES(...)                    \
extern int _dummy
#endif /* AUTOSTART_ENABLE */
#else
#error "C compiler must support __VA_ARGS__ macro"
#endif

这里用到C99 支持可变参数宏的特性,如:#define debug(…) printf(__VA_ARGS__) ,缺省号代表一个可以变化的参数表,宏展开时,实际的参数就传递给 printf()了。例:debug("Y = %d\n", y); 被替换成printf("Y = %d\n", y)【参考http://blog.chinaunix.net/uid-9112803-id-2898026.html】

为了简洁,我们这里按有AUTOSTART_ENABLE来分析

最终展开为:

struct process * const autostart_processes[] = {&hello_world_process, NULL};

hello_world_process是上边声明的process结构体变量,所以AUTOSTART_PROCESSES(&hello_world_process)就是定义了一个数组,这个数组定义了要自启动的进程的process结构体变量指针。

我们用一个例子来说明,进程是怎么自启动的。

源代码$contiki$\platform\stm32test\contiki-main.c

int
main()
{dbg_setup_uart();printf("Initialising\n");clock_init();process_init();process_start(&etimer_process, NULL);
  autostart_start(autostart_processes);printf("Processes running\n");while(1) {do {} while(process_run() > 0);idle_count++;/* Idle! *//* Stop processor clock *//* asm("wfi"::); */ }return 0;
}

可以看到,主函数在进行一系列初始化之后,启动etimer_process进程,然后启动需要自启动的进程。

我们进一步展开autostart_start函数,其中autostart_processes是上边声明定义的一个数组。

void
autostart_start(struct process * const processes[])
{int i;for(i = 0; processes[i] != NULL; ++i) {process_start(processes[i], NULL);PRINTF("autostart_start: starting process '%s'\n", processes[i]->name);}
}

可以看到依次启动autostart_processes数组中的各个指针所对应的进程,这里的process_start等函数,后续再深究。

四、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_THREAD是定义进程执行主体函数,跟上边的不一样,上边的是声明这样一个函数。

总之,PROCESS_THREAD有两个作用:声明或者定义进程执行主体函数

五、使用protothread机制的宏定义

1、PROCESS_BEGIN

#define PROCESS_BEGIN()             PT_BEGIN(process_pt)

其中process_pt是进程执行主体传进来的参数 struct pt *process_pt。protothread机制

这个是进程开始的标志。

2、PROCESS_END

#define PROCESS_END()               PT_END(process_pt)

进程结束标志。

注:进程执行主体语句要放在PROCESS_BEGIN和PROCESS_END之间,不然会有不确定的错误发生。

3、HelloWorld例子展开代码

(1)GCC c语言拓展实现版

#include "contiki.h"#include <stdio.h> /* For printf() */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", process_thread_hello_world_process };struct process * const autostart_processes[] = {&hello_world_process, NULL};static char process_thread_hello_world_process(struct pt *process_pt, process_event_t ev, process_data_t data)
{{ char PT_YIELD_FLAG = 1; if (PT_YIELD_FLAG) {;} do {if((process_pt)->lc != NULL) {goto *(process_pt)->lc;} } while(0)printf("Hello, world\n");PT_YIELD_FLAG = 0;PT_INIT(pt);return PT_ENDED; }}    

(2)switch语句实现版

#include "contiki.h"#include <stdio.h> /* For printf() */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", process_thread_hello_world_process };struct process * const autostart_processes[] = {&hello_world_process, NULL};static char process_thread_hello_world_process(struct pt *process_pt, process_event_t ev, process_data_t data)
{{ char PT_YIELD_FLAG = 1; if (PT_YIELD_FLAG) {;} switch((process_pt)->lc) { case 0:printf("Hello, world\n");}PT_YIELD_FLAG = 0;PT_INIT(pt);return PT_ENDED; }}

注:后续以switch语句为例子。

4、PROCESS_WAIT_EVENT

#define PROCESS_WAIT_EVENT()        PROCESS_YIELD()

我们假设在HelloWorld例子进程执行实体函数中插入这条语句:

PROCESS_THREAD(hello_world_process, ev, data)
{PROCESS_BEGIN();PROCESS_WAIT_EVENT();printf("Hello, world\n");PROCESS_END();
}
static char process_thread_hello_world_process(struct pt *process_pt, process_event_t ev, process_data_t data)
{{ char PT_YIELD_FLAG = 1; if (PT_YIELD_FLAG) {;} switch((process_pt)->lc) { case 0:do {                    PT_YIELD_FLAG = 0;process_pt->lc = _LINE_;case _LINE:if(PT_YIELD_FLAG == 0) {return PT_YIELDED;}} while(0);  printf("Hello, world\n");}PT_YIELD_FLAG = 0;PT_INIT(pt);return PT_ENDED; }} 

进程执行实体函数返回PT_YIELD,然后等待任一事件(进程是由事件驱动的)的到来,重新回到进程执行主体函数上次阻塞的位置_LINE_,又继续执行后续的语句。

注:由protothread机制知,每次执行进程实体函数时,都会运行到PROCESS_BEGIN,然后才跳转。

注:这里要明确一个概念,进程是由事件驱动的,只有当有事件发生时,进程执行实体函数才会开始执行,也就是说一旦发生阻塞,执行实体函数返回并退出,那么只有事件来了,才会再次进入进程执行实体函数,执行完PROCESS_BEGIN后跳转到阻塞位置,判断是否继续阻塞。

5、PROCESS_WAIT_EVENT_UNTIL

#define PROCESS_WAIT_EVENT_UNTIL(c) PROCESS_YIELD_UNTIL(c)

等待一个事件,并附加条件c

6、PROCESS_YIELD

#define PROCESS_YIELD()             PT_YIELD(process_pt)

YIELD(放弃执行权)

7、PROCESS_YIELD_UNTIL

#define PROCESS_YIELD_UNTIL(c)      PT_YIELD_UNTIL(process_pt, c)

YIELD直到条件c成立

8、PROCESS_WAIT_UNTIL

#define PROCESS_WAIT_UNTIL(c)       PT_WAIT_UNTIL(process_pt, c)

一直等待,直到条件c

9、PROCESS_WAIT_WHILE

#define PROCESS_WAIT_WHILE(c)       PT_WAIT_WHILE(process_pt, c)

当条件c,等待

10、PROCESS_EXIT

#define PROCESS_EXIT()              PT_EXIT(process_pt)

进程退出

11、PROCESS_PT_SPAWN

#define PROCESS_PT_SPAWN(pt, thread)   PT_SPAWN(process_pt, pt, thread)

#define PT_SPAWN(pt, child, thread)        \do {                        \PT_INIT((child));                \PT_WAIT_THREAD((pt), (thread));        \} while(0)

生产一个子进程,并阻塞住直到子进程执行完毕,再继续后边程序的执行。

转载于:https://www.cnblogs.com/songdechiu/p/5801136.html

process调用protothread机制的相关宏定义——用HelloWorld进程诠释相关推荐

  1. C 图像处理 颜色相关宏定义

    很多年前整理的,像素处理的宏定义,包括r8g8b8到r5g6b5之间的相互转化,浮点数像素与整数值之间的相互转化,像素值的插值.取反等处理.具体没什么好说的,宏定义的代码还是很容易看的.这套东西对搞图 ...

  2. Unity3D 多平台_预编译相关宏定义

    http://www.cnblogs.com/zhaoqingqing/p/3510332.html API地址:http://docs.unity3d.com/Documentation/Manua ...

  3. c语言宏定义在预处理阶段,预处理和宏定义

    8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? 宏定义 基本概念预处理部分: #include #define 放在函数之外,一般都放在源文档的前面 预处理由预处理进程 ...

  4. 宏定义函数和普通函数

    宏定义函数: 预处理阶段进行简单的文本替换,没有入栈.出栈.参数传递和函数返回等等工作,执行效率明显高于普通函数,因此,简短并且被频繁调用的函数经常用宏定义函数来代替实现 没有参数检查,会影响程序安全 ...

  5. Linux宏定义实现类成员函数,全面解析Linux内核的同步与互斥机制

    http://blog.csdn.net/sailor_8318/archive/2008/06/30/2599357.aspx [摘 要]本文分析了内核的同步及互斥的几种机制:原子运算符(atomi ...

  6. c语言assert_param,STM32断言机制assert_param()宏定义

    我们在学STM32的时候函数assert_param出现的几率非常大,上网搜索一下,网上一般解释断言机制,做为程序开发调试阶段时使用. 下面我就谈一下我对这些应用的看法,学习东西抱着知其然也要知其所以 ...

  7. c++ 带参数的宏定义实现反射机制

    lua 这种脚本语言用久了,总觉得反射机制就应该理所当然的嵌入在语言特性里. 比如希望根据自己传的类型名变量,动态去 new 一些实例.在 lua ,js 里做起来就非常简单,然而在 c++里面做起来 ...

  8. 子程序调用与宏定义的异同_冒泡排序_汇编语言的子程序调用与宏定义实现

    1 子程序 ;环境为ADS 子程序调用 AREA MAIN,CODE,READONLY ENTRY CODE32 start BL MAOPAO stop MOV R0,#0x18 LDR R1,=0 ...

  9. 内联函数和宏定义的相关区别

    内联函数和宏定义的区别 相同点 : 主要是为了避免被调用时的开销,是通过编译器来进行实现,而宏定义是通过预处理器进行替换,宏定义不检查函数参数,返回值等东西,只是展开,而相对来说,内联函数会进行检查参 ...

最新文章

  1. 十二张图带你了解 Redis 的数据结构和对象系统
  2. swift 之SnapKit 动画
  3. 42岁著名黑客去世,曾拯救互联网:揭开索尼“罪行”、发现DNS漏洞
  4. Linux系统常用命令备忘
  5. Diango 框架起步
  6. oracle log.xml分析,Oracle 11g Alert Log日志位置及参数
  7. 给ApplicationContext容器中添加组件的方法(@Bean的使用)
  8. 快速生成plist文件
  9. js 连mysql 延后_javascript – 异步并等待节点js中的MySQL调用
  10. 三星固态驱动安装失败_三星950 Pro固态硬盘用U盘装win7失败解决教程
  11. 新型基础测绘与实景三维中国建设技术文件【2】基础地理实体分类、粒度及精度基本要求
  12. 成功安装ML并驱动显卡(gtx460)声卡(alc892)之经验谈
  13. Axure RP8手机边框
  14. 阿里云跨账号迁移云服务器实例
  15. 绝对干货!初学者也能看懂的DPDK解析
  16. 幼儿园计算机技能大赛,幼儿园教师技能大赛的心得体会.docx
  17. 细读《深入理解 Android 内核设计思想》(四)Binder 机制 [中]
  18. MySQL之中间件Mycat实现读写分离
  19. 小米路由器登录服务器无响应是怎么回事,小米路由器登录界面打不开怎么办? | 192路由网...
  20. java 生成随机数字_java如何产生随机数

热门文章

  1. 使用.NET程序集作为Business Data Connectivity数据源(二)
  2. java获取硬盘序列号_Win7 64+Python3.7获取计算机硬盘信息初探
  3. gitlab-ee使用mysql_在 GitLab 我们是如何扩展数据库的
  4. spring处理循环依赖时序图_Maven依赖管理系统
  5. stack heap java_java中的Heap 和 Stack | 学步园
  6. DOS命令、Java语言开发环境(JVM、JDK、JRE)
  7. 常见的两个面试题以及面试的心态
  8. 云游戏关键技术研究报告(2020年)
  9. 面试题,你手里有几个offer?
  10. mysql begin end 用法_超实用的Mysql动态更新数据库脚本的示例讲解(推荐)