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. }


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部