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\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(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)

生產一個子進程,並阻塞住直到子進程執行完畢,再繼續后邊程序的執行。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM