前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >今天你要来点 protoThread么?

今天你要来点 protoThread么?

作者头像
GorgonMeducer 傻孩子
发布2020-09-29 15:01:49
发布2020-09-29 15:01:49
1.9K1
举报
文章被收录于专栏:裸机思维裸机思维

底软

模型

今天穿插一篇底软文章,“【硬件篇】VCU软件接口原理”尚未更新完,预计会在10篇以上,大家可以继续保持关注。本文介绍一种事件驱动模型“protothreads”,由瑞典SICS的Adam Dunkels开发,也是Contiki OS中的源代码,此模型适合应用于资源受限的嵌入式系统中,当然我也曾用于某项目的开发中,受益匪浅,独乐乐不如众乐乐,今天分享给大家,所讲细节不一定面面俱到,不足之处还请指教。

protothreads简介

任务栈:

protothreads是一种轻量级的线程模型,基于此模型可以实现类似于windows 中线程的编程风格,编程思路也倾向去线程。在OS中,每个线程都有独立的任务栈,然而protothreads的每个线程共享同一个任务栈,从而减少 RAM的 占用。

上下文切换:

在protothreads编程模型中,任务的本质是函数,函数与函数之间是协同工作关系,因而也叫“协程(coroutine)”;上下文的切换也有所不同,在OS中,上下文切换由OS来管理,但在 protothreads模型中,则是通过 yield 方式来保存现场和转移运行权。

任务阻塞:

protothreads 虽然提供了在各自线程内的条件阻塞机制,但对于在该线程内调用的其它函数,则无法阻塞其运行。所以,如果要在线程内调用占用时间较多的函数,为保证各个线程的实时性要求,需要将这类函数进一步划分为更小的函数,分步执行。protothreads 的阻塞实质就是函数返回,且仅能在程序员指定的位置阻塞。

代码分析

protothreads 包含5个头文件,是的,你没有看错,是头文件,意思就是没有任何C文件,整个模型是通过宏实现的,下表简单描述了这5个头文件的主要内容。

lc-addrlabels.h

使用GCC扩展语法实现的协程基础

lc-switch.h

使用switch语句实现的协程基础

lc.h

用于选择GCC语法还是switch语句实现

pt.h

基于lc.h实现协程API

pt-sem.h

协程间通信(信号量)的实现

主要API被包含在pt.h中,下面介绍一些常用的API:

  • 协程初始化

代码:#define PT_INIT(pt) LC_INIT((pt)->lc)

解释:初始化一个协程,其实就是初始化状态变量(pt)->lc,类似于MBD模型开发中的各个StateFlow状态;

  • 协程入口

代码:

#define PT_BEGIN(pt) \

{ char PT_YIELD_FLAG = 1; LC_RESUME((pt)->lc)

解释:协程的入口,PT_YIELD_FLAG =1,表示不出让运行权,PT_YIELD_FLAG =0表示出让运行权,LC_RESUME就是跳转到上一次出让运行权的位置继续运行,本质就是switch 跳转到相应的case。

  • 协程的退出口

代码:

#define PT_END(pt) \

LC_END((pt)->lc); PT_YIELD_FLAG = 0; \

PT_INIT(pt); return PT_ENDED; }

解释:协程的退出口,到此就算一个协程的结束,内容就是清除标志和上下文状态。

  • 协程条件阻塞1

代码:

#define PT_WAIT_UNTIL(pt, condition) \

do { \

LC_SET((pt)->lc); \

if(!(condition)) { \

return PT_WAITING; \

} \

} while(0)

解释:协程条件阻塞,阻塞的本质是出让CPU运行权,首先记录下当前的状态(LC_SET((pt)->lc)),以便下一次恢复,保存现场后再判断条件,如果condition为真,则继续往下运行,如果condition为假,直接return返回函数状态PT_WAITING,出让运行权,保持阻塞状态。所以根据字面上的意思可以这样理解:直到condition条件成立才往下执行,否则继续等待。

  • 协程条件阻塞2

代码:

#define PT_WAIT_WHILE(pt,condition) \

PT_WAIT_UNTIL((pt), !(condition))

解释:类似PT_WAIT_UNTIL,只是condition的条件取反而已。根据字面上的意思可以这样理解:当condition条件成立时继续等待,否则往下执行。

  • 协程退出

代码:

#define PT_EXIT(pt) \

do { \

PT_INIT(pt); \

return PT_EXITED; \

} while(0)

解释:任务后面的代码不执行,初始化状态变量,然后直接退出重新执行。

  • 协程无条件出让运行权

代码:

#define PT_YIELD(pt) \

do { \

PT_YIELD_FLAG = 0; \

LC_SET((pt)->lc); \

if(PT_YIELD_FLAG == 0) { \

return PT_YIELDED; \

} \

} while(0)

解释:协程运行权无条件出让1次,出让前,先记录下当前的状态(LC_SET((pt)->lc)),以便下一次恢复,保存现场后,立即出让运行权,本质就是函数return。

  • 协程有条件出让运行权

代码:

#define PT_YIELD_UNTIL(pt,condition) \

do { \

PT_YIELD_FLAG = 0; \

LC_SET((pt)->lc); \

if((PT_YIELD_FLAG == 0) || !(condition)) { \

return PT_YIELDED; \

} \

} while(0)

解释:比PT_YIELD(pt)多了一个条件, 协程运行权首先无条件出让1次,这是通过PT_YIELD_FLAG来实现的,第2次运行时,再判断条件condition,如果条件为真,往下执行,如果条件为假,则立即出让运行权继续阻塞。

实例

上面介绍了常用API,下面我们看一个实例:

上面的代码就不多解释了,注释已经写得够详细了吧,唯一需要解释的是这个宏“PT_WAIT_MS”,这是我通过PT_WAIT_UNITLL扩展出来的API,并结合系统Tick实现了延时功能,如果大家使用得比较熟悉之后,也可以利用基础的API去扩展自己的功能API。

大家不难发现,相对于使用状态机去实现以上同样的功能,使用protothreads能够节省很多行代码,而且易阅读,编程思路和效率也得到提高。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-09-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 裸机思维 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档