Contiki:Protothread切换机制理解

出处:Contiki:Protothread切换机制理解

在Contiki中,protothread的切换,实质是函数调用,通过call_process()函数调用protothread函数体的函数指针,来切换protothread,即ret = p->thread(&p->pt, ev, data);这里的p->thread指向的就是定义protothread的函数。而由于此函数中代码基本都是在PT_BEGIN和PT_END之间(宏展开后是一个完整的switch语句),所以对于保存状态的就是在本函数中运行的位置,通过__LINE__保存上一次运行到哪里,然后当再次调用这个protothread时,就可以通过switch跳到上一次执行的地方继续执行。

A protothread is driven by repeated calls to the function in which the protothread is running. Each time the function is called, the protothread will run until it blocks or exits. Thus the scheduling of protothreads is done by the application that uses protothreads.(contiki-2.6/doc/pt-doc.txt)

这和其他系统的任务切换概念是不一样的,像uCOS,Linux都是保存堆栈,保存寄存器信息,保存一个完整的任务运行状态下一次继续重复上一次的运行。而Contiki的这种Protothread更像是函数的调用,不过和一般的函数调用不一样的是,它下一次调用时进入函数运行的起点不是都一样,而是又上一次退出时(pt)->lc的值来决定。而且这时寄存器的值都不是上一次退出函数时的值,而是对于本次运行都是无用的,这也就是为什么Contiki在protothread中不建议使用局部变量的原因,因为只要退出本次protothread,下次重新运行时,即使是知道了上一次的退出时的位置,局部变量的值依然是未知的,因为没有保存。同时由于protothread使用switch语句实现,如果使用不当可能破坏protothread的跳转,这是为什么在protothread中不建议使用switch的原因。

当然,还有一种protothread的切换机制是使用goto语句实现的,这种实现机制下,使用switch语句是没有问题的。只是这种方式是基于GCC 编译器对于C的扩展实现的。

* This implementation of local continuations is based on a special
* feature of the GCC C compiler called "labels as values". This
* feature allows assigning pointers with the address of the code
* corresponding to a particular C label.

使用前要明确知道运行时系统使用的是哪一种切换机制。由于goto是基于GCC的特性,所以如果不是用GCC编译,就不用去尝试switch语句了。

A protothread runs within a single C function and cannot span over other functions.A protothread may call normal C functions, but cannot block inside a called function(contiki-2.6/doc/pt-doc.txt)

Contiki不能在被调函数中使用Block这样会造成protothread跳转错误。本质是因为使用switch语句或者goto语句。因为switch和goto语句有自己的限制,他们只能在函数内跳转,不能进行全局跳转。因为此,它们在实现BLOCK时,返回使用的是return语句,在被调函数中调用PT_WAIT_UNTIL也无法实现protothread的返回。

关于protothread的使用及注意事项在contiki-2.6/doc/pt-doc.txt说的很详细,仔细阅读下。

总之,不能使用以前的操作系统的概念来使用Contiki,要学着适应这种操作系统。每一种操作系统都有其最适合的地方,Contiki是为WSN设计的,对于WSN的实现而言,它应该是很优秀的。至于做其他的实现,不必为了用Contiki而用Contiki,选择适合的操作系统是我们需要做的,不必去苛求系统。

Protothread机制文档(contiki-2.6\doc\pt-doc.txt)

/**
\addtogroup sys
@{
*/
    
/**
\defgroup pt Protothreads

Protothread 是一种轻型,栈使用少的线程。这种线程为,如嵌入式系统或传感器结点,等内存受限系统而设计。

Protothread用C实现了事件驱动系统的线性执行速度的代码。Protothread可以用于或不用于RTOS(实时系统)。

Protothread是一种优秀的轻型,栈小的线程。它提供了在事件驱动系统上的阻塞机制,且没有传统线程的开销。Protothread的目的是不使用复杂的状态机或者传统线程(full multi-threading)。Protothread提供了在C函数内部的条件阻塞。

Protothread与单纯的事件驱动方法相比优势在于,它为阻塞函数提供了顺序代码结构。在纯粹的事件驱动系统中,必需人为地将函数分为两部分:一部分代码用于阻塞调用前,另一部分代码用于阻塞调用后。这使它很难使用if或者while等控制结构。

Protothread与传统线程( ordinary threads)相比的优势是Protothread不需要独立的栈空间。在内存受限系统中,分配多线程栈空间的开销会浪费大量可用的内存。相比之下,由于架构不同(即芯片架构),每一个Protothread只需要2个到12个状态字节。

\note

因为Protothread在阻塞调用时,不保存堆栈信息,所以在使用Protothread线程阻塞时,不保存局部变量。这意味着,在使用局部变量时,要十分小心。如果拿不准,在Protothread线程中,不要使用局部变量。

主要特性:

——没有特殊的汇编代码——Protothrea库文件,全部用C写成

——没有用易于出错的函数,像longjmp()

——很小的RAM开销——每个Protothread线程只要两个字节

——可以使用或不使用OS。

——提供没有传统多线程(full multi-threading )或栈切换的阻塞等待

应用举例:

——内存受限系统

——事件驱动协议栈

——深度嵌入式系统

——传感器网络节点

Protothread线程API有四种基本操作组成:

初始化:PT_INIT();

执行:PT_BEGIN();

条件阻塞:PT_WAIT_UNTIL();

退出:PT_END()。

除了以上这些,为了方便,实现了两个函数:

反相条件阻塞(reversed condition blocking,拿不准确):PT_WAIT_WHILE();

Protothread阻塞:PT_WAIT_THREAD();

\sa \ref pt "Protothreads API documentation"
 

Protothread线程库采用BSD-style License,它允许商业和非商业使用。The only requirement is that credit is given。

\section authors Authors

Protothread线程库,由Adam Dunkels<[email protected]>写就,得到了Oliver Schmidt<[email protected]>的帮助。

\section pt-desc Protothreads

Protothread是一种优秀的轻型,栈小的线程。它提供了在事件驱动系统上的阻塞机制,且没有传统线程的开销。Protothread的目的是不使用复杂的状态机或者传统线程(full multi-threading)。Protothread提供了在C函数内部的条件阻塞。

在内存受限系统中,如深度嵌入式系统,传统多线程(traditional multi-threading)或许存在很大的内存开销。在传统的多线程中,每个线程需要自己独立的栈空间。这些栈空间,或许使用了大量可用的内存。

Protothread与传统线程( ordinary threads)相比,主要优势在于Protothread十分轻量型:一个Protothread线程不需要独立的栈空间,而是所有的Protothread线程使用同一个栈空间。任务切换( context

switching)通过栈覆盖( stack rewinding)完成。这在内存受限系统中有很大的优势。在这些系统中,栈对应一个线程(全部内存只有一个堆栈,所有线程共同使用这一个堆栈),或许有大量可用内存。每个Protothread只需要两个字节。而且,Protothread完全使用C实现,没有任何特定的汇编代码。

Protothread运行在单一的C函数内,且不能跨函数使用。一个Protothread或许可以调用一般的C函数,但是不能在被调函数中阻塞。将潜在阻塞的函数写成一个单独的Protothread,替代调用内部嵌套阻塞的函数。这种写法的优势在于阻塞是显式的,我们清楚地了解哪一个函数阻塞了,哪一个函数不会阻塞。

Protothread线程有些像co-routins协程(本质上一种轻量级的线程,几种架构:多进程,多线程,非阻塞/异步IO(callback),Coroutine模型)。本质的区别在于,每个Coroutine协程需要独立的栈空间,而protothread使用栈空间很少。和protothread机制最像的是Python生成器。它也是一种使用栈空间少的结构,但是目的不一样。Protothread在一个C函数内部阻塞任务(blocking contexts),而Python生成器提供一个多退出口的生成函数。

\section pt-autovars Local variables

\note

由于Protothread在调用阻塞时,不保护栈内容。在protothread阻塞时,不保存局部变量。这意味着,在使用局部变量时,要十分小心。如果拿不准,不要在protothread中使用局部变量。

\section pt-scheduling Scheduling

每一次发生调用时,protothread将运行到它阻塞或退出。

因此,由使用protothread的应用程序,完成protothread的调度。

\section pt-impl Implementation

Protothread使用lc(local continuations)实现。一个lc(local continuation)表示执行时在程序中的实际位置(即函数的状态),但是没有保存任何调用记录和局部变量。lc(local continuation)在一个特定的函数内设定,以捕获函数的状态(即运行的实际位置)。在lc(local continuation)已经设置的地方,可以恢复到函数设置它的状态。

lc可以有多种实现方式:

——#使用特定的汇编代码

——#使用标准C的结构 

——#使用编译器扩展

第一种方法,是保存和恢复除栈指针外的处理器状态,每个protothread需要16-32个字节内存,确切需要的内存量取决于内核架构(芯片的内核结构)。

使用标准C实现,每个protothread仅需2字节。使用隐式的switch语句像是 Duff's device(见注1)。

然而,这种实现对于代码引入了一个小小的限制,即在使用protothread线程的代码中,不能使用switch语句。

一些编译器有C扩展,可以用来实现protothread。GCC支持标签指针(label pointers),它可以用于实现protothread。这种扩展使每个protothread需要4个字节(即一个指针的大小)的内存空间。

@{


*/

/** @} */
/** @} */

注:

1、http://blog.csdn.net/subkiller/article/details/5987944

Duff's device

附原文:

/**
\addtogroup sys
@{
*/
    
/**
\defgroup pt Protothreads

Protothreads are a type of lightweight stackless threads designed for
severly memory constrained systems such as deeply embedded systems or
sensor network nodes. Protothreads provides linear code execution for
event-driven systems implemented in C. Protothreads can be used with
or without an RTOS.

Protothreads are a extremely lightweight, stackless type of threads
that provides a blocking context on top of an event-driven system,
without the overhead of per-thread stacks. The purpose of protothreads
is to implement sequential flow of control without complex state
machines or full multi-threading. Protothreads provides conditional
blocking inside C functions.

The advantage of protothreads over a purely event-driven approach is
that protothreads provides a sequential code structure that allows for
blocking functions. In purely event-driven systems, blocking must be
implemented by manually breaking the function into two pieces - one
for the piece of code before the blocking call and one for the code
after the blocking call. This makes it hard to use control structures
such as if() conditionals and while() loops.

The advantage of protothreads over ordinary threads is that a
protothread do not require a separate stack. In memory constrained
systems, the overhead of allocating multiple stacks can consume large
amounts of the available memory. In contrast, each protothread only
requires between two and twelve bytes of state, depending on the
architecture.

\note Because protothreads do not save the stack context across a
blocking call, <b>local variables are not preserved when the
protothread blocks</b>. This means that local variables should be used
with utmost care - <b>if in doubt, do not use local variables inside a
protothread!</b>

Main features:

    - No machine specific code - the protothreads library is pure C

    - Does not use error-prone functions such as longjmp()
       
    - Very small RAM overhead - only two bytes per protothread
   
    - Can be used with or without an OS
   
    - Provides blocking wait without full multi-threading or
      stack-switching

Examples applications:

    - Memory constrained systems
   
    - Event-driven protocol stacks
   
    - Deeply embedded systems
   
    - Sensor network nodes

The protothreads API consists of four basic operations:
initialization: PT_INIT(), execution: PT_BEGIN(), conditional
blocking: PT_WAIT_UNTIL() and exit: PT_END(). On top of these, two
convenience functions are built: reversed condition blocking:
PT_WAIT_WHILE() and protothread blocking: PT_WAIT_THREAD().

\sa \ref pt "Protothreads API documentation"
   
The protothreads library is released under a BSD-style license that
allows for both non-commercial and commercial usage. The only
requirement is that credit is given.

\section authors Authors

The protothreads library was written by Adam Dunkels <[email protected]>
with support from Oliver Schmidt <[email protected]>.

\section pt-desc Protothreads

Protothreads are a extremely lightweight, stackless threads that
provides a blocking context on top of an event-driven system, without
the overhead of per-thread stacks. The purpose of protothreads is to
implement sequential flow of control without using complex state
machines or full multi-threading. Protothreads provides conditional
blocking inside a C function.

In memory constrained systems, such as deeply embedded systems,
traditional multi-threading may have a too large memory overhead. In
traditional multi-threading, each thread requires its own stack, that
typically is over-provisioned. The stacks may use large parts of the
available memory.

The main advantage of protothreads over ordinary threads is that
protothreads are very lightweight: a protothread does not require its
own stack. Rather, all protothreads run on the same stack and context
switching is done by stack rewinding. This is advantageous in memory
constrained systems, where a stack for a thread might use a large part
of the available memory. A protothread only requires only two bytes of
memory per protothread. Moreover, protothreads are implemented in pure
C and do not require any machine-specific assembler code.

A protothread runs within a single C function and cannot span over
other functions. A protothread may call normal C functions, but cannot
block inside a called function. Blocking inside nested function calls
is instead made by spawning a separate protothread for each
potentially blocking function. The advantage of this approach is that
blocking is explicit: the programmer knows exactly which functions
that block that which functions the never blocks.

Protothreads are similar to asymmetric co-routines. The main
difference is that co-routines uses a separate stack for each
co-routine, whereas protothreads are stackless. The most similar
mechanism to protothreads are Python generators. These are also
stackless constructs, but have a different purpose. Protothreads
provides blocking contexts inside a C function, whereas Python
generators provide multiple exit points from a generator function.

\section pt-autovars Local variables

\note
Because protothreads do not save the stack context across a blocking
call, local variables are not preserved when the protothread
blocks. This means that local variables should be used with utmost
care - if in doubt, do not use local variables inside a protothread!

\section pt-scheduling Scheduling

A protothread is driven by repeated calls to the function in which the
protothread is running. Each time the function is called, the
protothread will run until it blocks or exits. Thus the scheduling of
protothreads is done by the application that uses protothreads.

\section pt-impl Implementation

Protothreads are implemented using \ref lc "local continuations". A
local continuation represents the current state of execution at a
particular place in the program, but does not provide any call history
or local variables. A local continuation can be set in a specific
function to capture the state of the function. After a local
continuation has been set can be resumed in order to restore the state
of the function at the point where the local continuation was set.

Local continuations can be implemented in a variety of ways:

   -# by using machine specific assembler code,
   -# by using standard C constructs, or
   -# by using compiler extensions.

The first way works by saving and restoring the processor state,
except for stack pointers, and requires between 16 and 32 bytes of
memory per protothread. The exact amount of memory required depends on
the architecture.

The standard C implementation requires only two bytes of state per
protothread and utilizes the C switch() statement in a non-obvious way
that is similar to Duff's device. This implementation does, however,
impose a slight restriction to the code that uses protothreads in that
the code cannot use switch() statements itself.

Certain compilers has C extensions that can be used to implement
protothreads. GCC supports label pointers that can be used for this
purpose. With this implementation, protothreads require 4 bytes of RAM
per protothread.
 

@{


*/

/** @} */
/** @} */

猜你喜欢

转载自blog.csdn.net/wuhenyouyuyouyu/article/details/82345448
今日推荐