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的应用主函数
点击(此处)折叠或打开
- static
- PT_THREAD(handle_dhcp(void))
- {
- PT_BEGIN(&s.pt);
- do {
- send_discover(); //发送dhcpc探求包
- timer_set(&s.timer, s.ticks);
- PT_WAIT_UNTIL(&s.pt, uip_newdata() || timer_expired(&s.timer));
- //do something
- if(s.ticks < CLOCK_SECOND * 60) {
- s.ticks *= 2;
- }
- } while(s.state != STATE_OFFER_RECEIVED);
-
- do {
- send_request(); //发送dhcpc接受包
- timer_set(&s.timer, s.ticks);
- PT_WAIT_UNTIL(&s.pt, uip_newdata() || timer_expired(&s.timer));
-
- if(s.ticks <= CLOCK_SECOND * 10) {
- s.ticks += CLOCK_SECOND;
- } else {
- PT_RESTART(&s.pt);
- }
- } while(s.state != STATE_CONFIG_RECEIVED);
- while(1) {
- PT_YIELD(&s.pt);
- }
- PT_END(&s.pt);
- }
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) } 我们将上面那个函数展开,如下 -----------------------------------------------------------
点击(此处)折叠或打开
- static char handle_dhcp(void)
- {
- char PT_YIELD_FLAG = 1;
- switch(s.pt->lc){
- case 0:
- do{
- send_discover(); //发送dhcpc探求包
- timer_set(&s.timer, s.ticks);
- do{
- s.pt->lc = __LINE__;
- case __LINE__;
- if(!(uip_newdata())){
- return PT_WAITING;
- }
- }while(0)
- }while(s.state != STATE_OFFER_RECEIVED);
-
-
- do {
- send_request(); //发送dhcpc接受包
- timer_set(&s.timer, s.ticks);
- do{
- s.pt->lc = __LINE__;
- case __LINE__;
- if(!(uip_newdata())){
- return PT_WAITING;
- }
- }while(0)
-
- if(s.ticks <= CLOCK_SECOND * 10) {
- s.ticks += CLOCK_SECOND;
- } else {
- do {
- s.pt->lc = 0;
- return PT_WAITING;
- } while(0)
- }
- } while(s.state != STATE_CONFIG_RECEIVED);
-
- while(1){ //这个死循环是应用中的需求,dhcp后这个程序不要再执行了
- do {
- PT_YIELD_FLAG = 0;
- s.pt->lc = __LINE__;
- case __LINE__:
- if(PT_YIELD_FLAG == 0) {
- return PT_YIELDED;
- }
- } while(0) //可以看出此宏功能是,此次程序执行到这里就返回,下次再到本协程函数,不返回继续往后执行。
-
- }
-
- }
- PT_YIELD_FLAG = 0;
- s.pt->lc = 0;
- return PT_ENDED;
- }
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
