一、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//取名字
#endif
PT_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() {
do {
} while(process_run() > );
idle_count++;
/* Idle! */
/* Stop processor clock */
/* asm("wfi"::); */
}
return ;
}

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

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

void
autostart_start(struct process * const processes[])
{
int i; for(i = ; 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 = ;
    if (PT_YIELD_FLAG) {;}     do {
if((process_pt)->lc != NULL) {
goto *(process_pt)->lc;
}
} while()     printf("Hello, world\n");     
    PT_YIELD_FLAG = ;
    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 = ;
    if (PT_YIELD_FLAG) {;}     switch((process_pt)->lc) {
case :       printf("Hello, world\n");     }     PT_YIELD_FLAG = ;
    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 = ;
    if (PT_YIELD_FLAG) {;}     switch((process_pt)->lc) {
case :     do {
      PT_YIELD_FLAG = ;
       process_pt->lc = _LINE_;
         case _LINE:
       if(PT_YIELD_FLAG == ) {
       return PT_YIELDED;
       }
     } while();         printf("Hello, world\n");     }     PT_YIELD_FLAG = ;
    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()

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

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. UI控件相关宏定义

    1.显示设置 1.1 view圆角和边框 /** 设置view圆角和边框 */ #define kViewBorderRadius(View, Radius, Width, Color)\ \ [Vi ...

  4. QT跟VC++结合来进行插件的验证机制(遍历vtable,保证虚函数的个数一致,也可使用Q_INVOKABLE宏定义)

    由于最近公司要开发一个以C++插件机制为主的,主要有一个问题就是C++的二进制兼容性的问题.一旦类使用虚函数,只要随便改动下增删查改下头文件的虚函数,就会导致程序在跑的时候进行乱跳,因为这个时候exe ...

  5. C语言宏定义相关

    写好C语言,漂亮的宏定义很重要,使用宏定义可以防止出错,提高可移植性,可读性,方便性 等等.下面列举一些成熟软件中常用得宏定义......1,防止一个头文件被重复包含#ifndef COMDEF_H# ...

  6. c++ 宏定义调用不定参数的函数

    假设有一下函数 void Logging(int nLevel, const char* szFormat, ...); 则宏定义如下 #define LOG(FCFL) Logging##FCFL ...

  7. Protothread 机制

    一.概述 很多传感器操作系统都是基于事件驱动模型的,事件驱动模型不用为每个进程都分配一个进程栈,这对内存资源受限的无线传感器网络嵌入式系统尤为重要. 然而事件驱动模型不支持阻塞等待抽象语句,因此程序员 ...

  8. 宏定义中的##操作符和... and _ _VA_ARGS_ _

    1.Preprocessor Glue: The ## Operator 预处理连接符:##操作符 Like the # operator, the ## operator can be used i ...

  9. iOS开发笔记--宏定义的黑魔法 - 宏菜鸟起飞手册

    宏定义在C系开发中可以说占有举足轻重的作用.底层框架自不必说,为了编译优化和方便,以及跨平台能力,宏被大量使用,可以说底层开发离开define将寸步难行.而在更高层级进行开发时,我们会将更多的重心放在 ...

随机推荐

  1. 【温故知新】——Bootstrap响应式知识点复习

    前言:本文是自己在学习课程中的课程笔记,这里用来温故知新的,并非本人原创. 开发工具 1.记事本,Editplus,... ... 2.Sublime,Dreamweaver 3.Webstorm = ...

  2. xml 文件不给提示(以mybatis 的 mapper映射文件为例)

    在xxx.xml 映射文件的头部可以看到 如下: (mybatis generate 自动生成) <!DOCTYPE mapper PUBLIC "-//mybatis.org//DT ...

  3. 【秀优越(xie e)】原来出题也能够这么恶心。

    通过邪恶的数据范围和数据限制居然能够把一道传统题出成题答2333. 诶毕竟内部互測,题目就不往上贴了. 特殊限制 - - - 题目作废.输出M行"Orz  PoPoQQQ" - M ...

  4. Nexus 5刷阿里云OS

    刷机有风险,刷机需慎重! 1. 下载 recovery 的img和 阿里云os. recovery 的img下载: https://kanbox.com/f/V00KA 阿里云OS3.0.3 : 2月 ...

  5. Shell脚本之:变量

    与编译型语言不同,shell脚本是一种解释型语言. 执行这类程序时,解释器(interpreter)需要读取我们编写的源代码(source code),并将其转换成目标代码(object code), ...

  6. 【Python】写入文件

    1.1写入空文件 若将文本写入文件,在调用open()时候需要提供另外一个实参,告诉Python你要写入打开的文件 file_path = 'txt\MyFavoriteFruit.txt' with ...

  7. UDP通信注意事项

    今天调试UDP,笔记本上面可以实现但台式机上面竟然无法通信,后来找了半天,原来是权限问题.必须将用户权限设置为最低才行. 在运行里面输入UAC (user access control )用户权限设置 ...

  8. oracle 使用job定时自动重置sequence

    一.赋予用户创建和删除sequence的权限 grant create any sequence to user_name; grant drop any sequnce to user_name; ...

  9. 菜鸟运维笔记:配置Apache二级域名及WWW訪问

    事实上www訪问也能够理解为二级域名的一种. 域名 比方.com..org..edu..gov..info..net等等都是一级域名,或称顶级域名. 其详细格式是 *.顶级域名,比方csdn.net. ...

  10. 认识 service worker

    离线缓存可以提升用户体验,可以节省网络资源,但是,浏览器对资源缓存和自定义网络请求的控制一直不够完善,service worker 的出现就是为了解决这些问题 它可以解决目前离线应用的问题,同时也可以 ...