(转)Protothreads极轻量级系统,用于资源紧张的单片机。
在别的网站看到的一篇文章,不知对本坛是否有用,希望能有所启发。以下内容是转帖。Protothreads极轻量级系统用于资源紧张的单片机。
080101-KV2004 @daxia.com
Ptotothreads的作者是Adam Dunkels <adam@sics.se>,很感谢作者给我们提供这样一个好的系统,我认为合适的才是最好的。
作者描述Protothreads的特性是:
1 没有专用的机器代码,纯C实现;
2 不使用容易犯错的跳转指令;
3 极小的内存占用;
4 当不当做操作系统来用都行;
5 所提供的阻断等待不需要堆栈或者full multi-threading(谁能翻译一下,刚刚接触操作系统)。
作者提供了几个例子,以帮助我们理解。
先看一下作者提供的5个头文件,这5个头文件就是Protothreads的全部文件。
lc-addrlabels.h :以字符串方式实现Protothreads系统,占用的ram可能会多些。
lc-switch.h :以标准C的switch结构实现Protothreads系统(默认)
lc.h :选择lc-addrlabels.h或lc-switch.h两种实现Protothreads系统
pt.h :在实际应用中一般只包含此文件就行了
pt-sem.h :附加的信号量操作的支持,不需要的话则不必包含他
虽然我不熟悉其他的操作系统,但是,单看一眼其他系统的源代码,就能看到有不少压栈和出栈的指令。我认为,对于小小单片机来说,不太合适,压栈和出栈的指令明显占用了时间,更占用了大量的本来不宽裕的ram。并且,似乎用于ram紧张的单片机,还会
留有隐患。
对于我来说,使用“系统”只是为了让我的程序思路更清晰,不然一堆变量,信号量和信号量之间互相制约,有没有层次感,时间一长,思路断了,只能重新编写。
Protothreads提供给我们另外一种思路:我们的程序经过了Protothreads的包装,而实际上Protothreads的“线程”就是普通的函数。
所有的“线程”都是(也允许不是)一个死循环,不多占用堆栈,不能被抢占,所以中途必须“退出”,不然怎么会有其他“线程”被调度的机会?退出有两种结果:下一次被调度后从头运行或从退出的位置继续向后运行。Protothreads使用C语言中的
“switch”结构或goto语句来实现。既然能够从上一次退出的位置继续向后运行,那么就需要一个变量来记录这个“位置”信息,这个变量就是
线程唯一占用的ram变量。作者提供的数据类型是短整型或字符串类型。我认为这就是Protothreads的精髓,也就是说Protothreads
线程不是被其他程序“中断”的,而是自己主动退出(有点雷锋精神),但退出之前记录当前位置,以便下次从此位置继续运行(真是矛盾
的统一体)。
Protothreads为我们提供方便的同时也有一些需要我们改变编程习惯的地方,比如,我们不能在正常线程里面放一个类似“while(1);”的程序,除非我们确实需要这样做--期待看门狗复位。但是,Protothreads提供给我们一个理解Protothreads的机会,源代码完全开放,实现“多任务”的方式方式又是如此的简单明了。我们完全可以根据自己的需要修改库文件。并且以我们对Protothreads的
理解,作出我们“心中有数”的程序。
上面描述过作者提供的5个头文件,而我只想把Protothreads用在ram和速度都紧张的单片机(如avr的m16)上,所以,我去掉
了两个头文件,只留下了三个:
lc.h :以标准C的switch结构实现Protothreads系统
pt.h :Protothreads的接口,必须包含此头文件
pt-sem.h :附加的信号量操作的支持,不需要的话则不必包含他
在pt.h中包含所有实现Protothreads的宏和变量结构的定义:
可以阅读一下作者提供的帮助文档,已经非常详细了,结合作者提供的例子,很快就能理解了,几乎都是字面意思,我保证在你真正了解C语言的前提下,不用半天就能够理解Ptotothreads。
我只说一下我对PT_YIELD(pt)的理解:放弃当前执行后续代码的机会,退出,但是下一次调度到本线程,将从此处继续运行。
有了这个宏就给了其他线程被调度的机会,如果一个线程中包含PT_WAIT_UNTIL(pt)或PT_WAIT_WHERE(pt)之类的宏,则没有必要再调用PT_YIELD(pt)了。另外,作者为了实现PT_YIELD(pt),而定义了一个局部变量PT_YIELD_FLAG,我认为没有必要,所以我对pt.h
相关地方做了修改,已经把局部变量PT_YIELD_FLAG去掉了。
作者提供的库没有提供线程的自挂起和恢复挂起功能,这个动作可以使用作者提供的PT_YIELD_UNTIL(pt,cond)宏,来实现,或利用一下信号量的操作,使用pt-sen.h头文件所包含的宏来实现。但以我对Protothreads的理解,把pt.h做了一点修改,提供了与PT_YIELD(pt)相似的方法,实现了线程的“自己”挂起和“他人”恢复。既然看懂了系统的源程序,那么我们的思路还可以再扩展一下,函数当中什么地方只运行一次(PT_BEGIN(pt)后面),什么地方每次调度都能运行(PT_BEGIN(pt)前面),为什么作者说局部变量不能乱用等等。我们都能从系统的源程序里面找到答案。
AVR GCC对C支持的很好。Protothreads可以很好的用在avrgcc上。实际效果也很好,程序看起来更清晰了,多个线程配合的也很好。
我对操作系统了解不多,对其他的操作系统也没有深入接触过,有些名词会有错误。但我认为,Ptotothreads极容易掌握,对
于像avr的m16之类的单片机,Protothreads是很好的选择。
这篇文章是基于作者的Protothreads-1.4版写的,作者还在不断的更新中,作者网站是:http://www.sics.se/~adam/pt/ 居然是沙发,顶一次! 4 当不当做操作系统来用都行;
5 所提供的阻断等待不需要堆栈或者full multi-threading(谁能翻译一下,刚刚接触操作系统)。
作者提供了几个例子,以帮助我们理解。
直接出来没人看得懂,应译为"真正的多进程机制".thread本是线程之意,这么简单的系统里就别分什么进程线程了,一码事. 等待高人评论,学习。 Protothreads确实是令人吃惊的作品
它应该是基于直接跳转方式来实现“多线程”,即所有线程实际上是在一个栈上面。
这样的实现仅能够实现那种必须一个“线程”运行结束,或者说遇到了阻塞点的时候,才能切换到其他的线程运行。
所以,这种实现方式可以看成是和操作系统无关的,即使在已有的操作系统上也能够采用这种模型。当然直接在裸机上也能够使用。
Adam Dunkels 是一位令人尊敬的科学家,lwip、uip以及使用protothreads的contiki操作系统都是出自他之手,在这些软件中,把“小”的概念体现得淋漓尽致,或许正式对小的概念的极端追求,所以后续lwip不再维护,而转向了uip。 以前我在一家公司见过一个商用产品就用的这种线程模型,非常精炼,但是局部变量用起来必须非常非常谨慎,感觉不熟悉它的人在上面直接写代码挺危险的 To LZ:
本坛以前有过相关讨论,建议略搜索一下。
PT有重入的问题,使用的时候需要额外注意。一个是两个人调用同一个PT;一个是局部变量的数值。
PT的实现有两种方式
1:基于标准C的switch实现,当阻塞点过多的时候,实际比较效率不高。
2:基于Addressable Label的实现,要求编译器支持。
PT页面还有很多更多实现的链接,也很有参考价值的。
总的来说是个很不错的东西。 学习了。 好东西,想学 http://www.sics.se/~adam/
In 2009, I was listed as one of the TR35 top 35 innovators under 35 by the MIT Technology Review. In 2008, I received the Xerox Chester Carlson award, for my work on IP networking for embedded systems, the 2008 ACM SIGOPS EuroSys Roger Needham award for the best 2007 European doctoral thesis in computer systems, and the 2008 ERCIM Cor Baayen award.
Adam Dunkels 大名鼎鼎的Adam,天才级人物。。。
不知什么时候中国也出这样的人物,有智商的下海的下海,当官的当官 学习了。 回复【5楼】mbbill
以前我在一家公司见过一个商用产品就用的这种线程模型,非常精炼,但是局部变量用起来必须非常非常谨慎,感觉不熟悉它的人在上面直接写代码挺危险的
-----------------------------------------------------------------------
对,我也如此用过,因为配合uip协议栈,Protothreads还是比较适合的。因为它的“线程”没有自己的栈,所以没有局部也不会受到保护,必须很小心!最好别依赖局部变量来记录运行状态相关的信息,所以“线程”也就没有可重入特点了。 PT如果要可重入,变量需要分配在堆上,同时在保存Continuation Point的变量里增加位置保存堆指针。
潜在的问题是碎片,别的还好说了。 mark 我以前用过,这个不算操作系统,应该是一个调度器, mark 学习了 mark #define PT_END(pt) LC_END((pt)->lc); PT_YIELD_FLAG = 0; \
PT_INIT(pt); return PT_ENDED; }
#define PT_WAIT_UNTIL(pt, condition) \
do { \
LC_SET((pt)->lc); \
if(!(condition)) { \
return PT_WAITING; \
} \
} while(0)
---------------------------------------以上是源码
我想提2个问题:
1.PT_YIELD_FLAG = 0; 这句好像没有任何用处,它是局部变量,线程退出后自动销毁,干么给他赋值0,再退出销毁呢?
2.第二个宏定义中do{}while(0)这种结构貌似可以不要,作者为什么这样写呢? mark 不错哦~~ 最近也开始接触这个东西,细细究竟还是很不错的. jishanlaike 发表于 2012-2-23 19:40 static/image/common/back.gif
#define PT_END(pt) LC_END((pt)->lc); PT_YIELD_FLAG = 0; \
PT_INIT(pt); return PT_ ...
我也同问,这几天在看总觉得有些疑惑。 jishanlaike 发表于 2012-2-23 19:40 static/image/common/back.gif
#define PT_END(pt) LC_END((pt)->lc); PT_YIELD_FLAG = 0; \
PT_INIT(pt); return PT_ ...
do while这个结构是为了宏定义语句可以放在任何地方
对于一个宏定义里有很多条语句组成的话,用do while有很好的移植性 这个 确实好哦 但是不知道 lc 后面的 case: __LINE___ 怎么搞的?vc++ 报错呢。 看看。。 protothread 确实是不错的东西,这个库的作者真厉害,学习思想 沙发,研究下! 有空好好研究,赞 mark PT对于资源紧张的MCU确实带来编程的革命 mark一下
PT对于资源紧张的MCU确实带来编程的革命 orz
用uip的时候都没留意它的存在..
页:
[1]