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


    一、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
    ");
      
      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 ", y); 被替换成printf("Y = %d ", 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$platformstm32testcontiki-main.c

    int
    main()
    {
      dbg_setup_uart();
      printf("Initialising
    ");
      
      clock_init();
      process_init();
      process_start(&etimer_process, NULL);
      autostart_start(autostart_processes);
      printf("Processes running
    ");
      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'
    ", 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
    ");
    
        
        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
    ");
    
        }
    
        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
    ");
      
      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
    ");
    
        }
    
        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)

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

  • 相关阅读:
    Python 接口测试之处理转义字符的参数和编码问题
    使用Postman工具做接口测试(三)——断言与参数提取
    Django 单元测试笔记
    django 发布会签到系统web开发
    Django自动化测试平台项目案例
    Cypress学习笔记6——Debugging调试代码
    Cypress学习笔记5——官方示例
    Cypress学习笔记4——编写第二个测试脚本(登录案例)
    Cypress学习笔记3——编写第一个测试脚本
    Cypress学习笔记2——Windows环境下安装Cypress
  • 原文地址:https://www.cnblogs.com/songdechiu/p/5801136.html
Copyright © 2020-2023  润新知