uip之protothreads

本文简单介绍一下网络协议栈uip中的protothreads(协程)部分。
通常我们等待一个事件时有阻塞和非阻塞两种方式,uip不支持多线程操作,也不依靠中断来通知事件,所以要使用阻塞的方式。但阻塞这种方式又会白白浪费cpu时间阻塞在那里等待事件发生。因而uip使用了一种protothreads方式。我们暂称其协程。
下面是官方文档的一些简介。

协程是一种无堆栈的轻量级线程,它被设计用在服务一些简单有限的内存体系上,如一些嵌入式系统等。协程为事件驱动的线性代码执行,提供C的实现。协程可以被用在有或无RTOS(实时操作系统)的结构上。协程是一个非常轻量级,无堆栈线程,提供了事件驱动系统顶层的阻塞上下文的功能。而不需要每个线程的堆栈。协程的目的是实现连续流程的控制,而不需要状态机或者完整的多线程机制支持。在C函数中协程提供条件阻塞。

翻译的很拗口,直接写吧。


要使用协程,先看下以下几个基本API,初始化PT_INIT().执行PT_BEGIN(),条件阻塞PT_WAIT_UNTIL(),和结束PT_END().
我们从uip的一个应用实例dhcpc中看一下这个协程是如何使用及其原理(删去了无关代码)

在dhcpc初始化函数中调用了
PT_INIT(&s.pt);
下面是dhcpc的应用主函数

点击(此处)折叠或打开

  1. static
  2. PT_THREAD(handle_dhcp(void))
  3. {
  4.   PT_BEGIN(&s.pt);
  5.   do {
  6.     send_discover();    //发送dhcpc探求包
  7.     timer_set(&s.timer, s.ticks);
  8.     PT_WAIT_UNTIL(&s.pt, uip_newdata() || timer_expired(&s.timer));

  9.         //do something
  10.     if(s.ticks < CLOCK_SECOND * 60) {
  11.       s.ticks *= 2;
  12.     }
  13.   } while(s.state != STATE_OFFER_RECEIVED);
  14.  
  15.   do {
  16.     send_request();    //发送dhcpc接受包
  17.     timer_set(&s.timer, s.ticks);
  18.     PT_WAIT_UNTIL(&s.pt, uip_newdata() || timer_expired(&s.timer));
  19.        
  20.     if(s.ticks <= CLOCK_SECOND * 10) {
  21.       s.ticks += CLOCK_SECOND;
  22.     } else {
  23.       PT_RESTART(&s.pt);
  24.     }
  25.   } while(s.state != STATE_CONFIG_RECEIVED);
  26.   while(1) {
  27.     PT_YIELD(&s.pt);
  28.   }
  29.   PT_END(&s.pt);
  30. }
我们分别看下这几个宏的实现。
1.PT_INIT
初始化协程宏,必须在执行相应的协程函数前执行此宏
#define PT_INIT(pt)   LC_INIT((pt)->lc)
我们看到这里又有一个新的宏LC_INIT(这是Local continuations(局部连续块)部分),其实就是初始化状态的
#define LC_INIT(s) s = 0;
结构pt的定义
struct pt {
lc_t lc;
};
所以PT_INIT(&s.pt);这个宏展开就是
s.pt->lc = 0;

2.PT_THREAD
定义协程函数的宏,凡是要使用协程的函数都要被此宏定义(修饰),宏定义如下
#define PT_THREAD(name_args) char name_args

3.PT_BEGIN
协程开始宏,定义如下
#define PT_BEGIN(pt) { char PT_YIELD_FLAG = 1; LC_RESUME((pt)->lc)
其中LC_RESUME宏,定义如下
#define LC_RESUME(s) switch(s) { case 0:
所以PT_BEGIN(&s.pt);宏展开如下
char PT_YIELD_FLAG = 1;
switch(s.pt->lc){
case 0:
可以看出这个宏就是定义了一个switch的头。

4.PT_WAIT_UNTIL
等待条件成立宏,此宏将“阻塞”在这个条件下
#define PT_WAIT_UNTIL(pt, condition)         \
  do { \
    LC_SET((pt)->lc); \
    if(!(condition)) { \
      return PT_WAITING; \
    } \
  } while(0)
其中LC_SET宏为,
#define LC_SET(s) s = __LINE__; case __LINE__:
其中__LINE__是编译器内部产生的变量,它表示当前程序的行数。
所以将宏展开为
do{
s = __LINE__;
case __LINE__;
if(!(condition)){
return PT_WAITING;
}
}while(0) 

5.PT_RESTART
重启协程宏,定义
#define PT_RESTART(pt) \
  do { \
    PT_INIT(pt); \ //关键,将条件归0
    return PT_WAITING; \
  } while(0) 

6.PT_YIELD
放弃执行宏,此宏功能是放弃此次执行函数返回。
#define PT_YIELD(pt) \
  do { \
    PT_YIELD_FLAG = 0; \
    LC_SET((pt)->lc); \
    if(PT_YIELD_FLAG == 0) { \
      return PT_YIELDED; \
    } \
  } while(0)
 
7.PT_END
协程结束宏
#define PT_END(pt) LC_END((pt)->lc); PT_YIELD_FLAG = 0; \
                   PT_INIT(pt); return PT_ENDED; }
其中LC_END宏定义为
#define LC_END(s) }
 
我们将上面那个函数展开,如下
-----------------------------------------------------------

点击(此处)折叠或打开

  1. static char handle_dhcp(void)
  2. {
  3.     char PT_YIELD_FLAG = 1;
  4.     switch(s.pt->lc){
  5.     case 0:
  6.         do{
  7.             send_discover();    //发送dhcpc探求包
  8.         timer_set(&s.timer, s.ticks);
  9.         do{
  10.                     s.pt->lc = __LINE__;
  11.     case __LINE__;
  12.                     if(!(uip_newdata())){   
  13.                     return PT_WAITING;
  14.                     }
  15.             }while(0)
  16.         }while(s.state != STATE_OFFER_RECEIVED);
  17.        
  18.        
  19.         do {
  20.     send_request();    //发送dhcpc接受包
  21.     timer_set(&s.timer, s.ticks);
  22.     do{
  23.                     s.pt->lc = __LINE__;
  24.     case __LINE__;
  25.                     if(!(uip_newdata())){   
  26.                     return PT_WAITING;
  27.                     }
  28.         }while(0)
  29.        
  30.     if(s.ticks <= CLOCK_SECOND * 10) {
  31.       s.ticks += CLOCK_SECOND;
  32.     } else {
  33.       do {                       
  34.             s.pt->lc = 0;           
  35.             return PT_WAITING;           
  36.           } while(0)
  37.     }
  38.   } while(s.state != STATE_CONFIG_RECEIVED);
  39.  
  40.   while(1){                                    //这个死循环是应用中的需求,dhcp后这个程序不要再执行了
  41.       do {                       
  42.         PT_YIELD_FLAG = 0;   
  43.         s.pt->lc = __LINE__;
  44.    case __LINE__:               
  45.         if(PT_YIELD_FLAG == 0) {           
  46.           return PT_YIELDED;           
  47.         }                       
  48.       } while(0)                            //可以看出此宏功能是,此次程序执行到这里就返回,下次再到本协程函数,不返回继续往后执行。
  49.      
  50.   }
  51.  
  52.     }
  53.   PT_YIELD_FLAG = 0;
  54.   s.pt->lc = 0;
  55.   return PT_ENDED;

  56. }

你可能感兴趣的:(C语言)