序章:Protothreads的原生特性

Protothreads的原生特性

背景介绍

Protothreads 是一个无堆栈的系统,原创作者是Adam Dunkels adam@sics.se。根据原作者的介绍,Protothreads的原生特性主要有:

  • 没有专用的机器代码,纯C实现;
  • 不使用容易犯错的跳转指令; 任务调度的switch case 用宏封装
  • 极小的内存占用;最小线程开销2字节的
  • 当不当做操作系统来用都行;可以和混用轮询的裸机程序
  • 所提供的阻断等待不需要堆栈或者full multi-threading。
    原作者在github的开源仓库 https://github.com/gburd/pt.git

原生文件介绍

支持Protothreads系统的一共有5个头文件,分别是lc-addrlabels.h、lc-switch.h、lc.h、pt-sem.h、pt.h。其中lc-addrlabels.h和lc-switch.h功能一致,实现的方式略有差异。pt-sem.h文件时使用其无堆栈系统的特性实现的信号量。如果想使用Protothreads的原生系统,只需要移植文件lc-switch.h、lc.h、pt.h即可。

原生接口介绍

原生Protothreads暴露的接口主要时在pt.h文件里。主要有以下这些接口

接口函数功能描述
PT_BEGIN(pt)线程开始时调用: switch {
PT_YIELD(pt)调用后让出CPU一次 : case
PT_WAIT_UNTIL(pt, condition)调用后让出CPU直到条件成立:case
PT_SPAWN(pt, child, thread)调用后CPU让给子线程:case
PT_RESTART(pt)重置线程运行记录标记
PT_EXIT(pt)调用异常结束线程
PT_INIT(pt)初始化线程运行记录标记
PT_END(pt)调用后正常结束线程:}

一个protothreads的必须得由,PT_BEGIN开始,PT_END结束。如果中间代码块使用while(1)的死循环函数,必须还要有PT_YIELD或PT_SPAWN或PT_WAIT_UNTIL等具备case标记的阻塞函数。以下是一个最小的protothreads的结构

static PT_THREAD(demo_thread(struct pt *pt))
{
    static uint8_t s_cond = PT_FALSE;
    PT_BEGIN(pt);
    while (1)
    {
        PT_YIELD(pt);
        PT_WAIT_UNTIL(pt, s_cond);
    }
    PT_END(pt);
}

上述线程里,会一直阻塞在PT_WAIT_UNTIL(pt,s_cond);因为s_cond 是局部static变量,条件永远无法变真。

调度原理分析

既然接口都是用宏来实现的,根据编译原理,我们可以使用gcc -E的方式把宏展开,通过展开后的宏,我们可以一浏览其结构。从而分析Protothreads的调度原理,从根本上理解无堆栈系统的含义。由于Proththreads使用了__LINE__宏,为了贴切表述具体含义,下述两个分析例子将使用贴图的方式。

例子一:最小的线程结构

原始的min_thread
在这里插入图片描述
gcc -E 展开后的min_thread
在这里插入图片描述
如果在未展开的代码中去掉LOG的标记, 其简洁版如下

static PT_THREAD(min_thread(struct pt *pt))
{
    PT_BEGIN(pt);
    PT_YIELD(pt);
    PT_END(pt);
}

对比原始代码和gcc -E后的代码.我们先在应用的层面理解

  1. 我们对展开后的代码进行脑颅测试,如果轮询调用函数,第一次会从正常运行到2080行return,第二次则会在2071行直接跳转到2079行,并且顺利走到2091行。
  2. 对照简洁版的代码,我们可以看出min_threads的功能是第一次运行PT_BEGIN然后再PT_YIELD跳出,第二次会直接从PT_YIELD下面运行到PT_END

对比原始代码和gcc -E后的代码.我们先在原理的层面理解

  1. 原始的10行是展开后的2069到2072,也就是说PT_BEGIN 提供一个不完整的switch语句头部
  2. 原始的11到13行对应着2073到2075 ,配合着PT_YEILD展开看来,原始的11到13行仅会运行一次
  3. 同理原始的15到17行对应着2084到2086,配合着PT_END展开看来,原始的15到17行仅会运行一次,且PT_END后面的绝对不会运行到

由此可见最小的prothreads ,PT_BEGIN和PT_END 成对存在,组成一个完成的switch case 语句。而PT_YIELD提供一个让出CPU的功能。prothreads提供了一个switch case的任务调度的雏形

例子二:线程带while(1)结构

原始的min_thread
在这里插入图片描述

gcc -E 展开后的min_thread
在这里插入图片描述

如果在未展开的代码中去掉LOG的标记, 其简洁版如下

static PT_THREAD(while_thread(struct pt *pt))
{
    static uint8_t s_cond = PT_FALSE;
    PT_BEGIN(pt);
    while (1)
    {
        PT_YIELD_UNTIL(pt, s_cond);
    }
    PT_END(pt);
}

对比原始代码和gcc -E后的代码.我们先在应用的层面理解

  1. 我们对展开后的代码进行脑颅测试,如果轮询调用函数,第一次会从正常运行到2013行return,第二次则会在2093行直接跳转到2101行,并且2013行return。(除非s_cond成真)
  2. 对照简洁版的代码,我们可以看出min_threads的功能是第一次运行PT_BEGIN然后再PT_YIELD_UNTIL跳出,第二次会直接从PT_YIELD_UNTIL判断s_cond的条件。除非s_cond成真才会出来。
  3. 假设s_cond成真了,程序通过了2106,在2107的时候,会再次出现在2096的while(1)里面。结果再次进行s_cond的判断

由此可见最小的prothreads ,PT_BEGIN和PT_END 成对存在,该对里面还是遵循C语言的规则的。(注意:switch语句有条件遵循)

优缺点分析

Prothreads基础调度原理类似状态机的方式,但通过宏的封装,让使用者可以从状态机的切换中解脱出来。按照类操作系统的方式进行程序设计,无需费劲心思设计异步的方案。同样的通过调度的原理(宏的展开),我们非常贴切的明白了Prothreads的无栈 含义。**线程里局部变量将会被释放!!!**以下整理出来的优点和缺点

优点缺点
极少的线程开销无堆栈
纯C实现无专用机器码
完美兼容轮询式裸机
switch语法受限

switch 受限的例子见附录,具体原因自行分析

结论

根据Prothreads的自身原理分析,其缺点我们可以通过代码编写规范进行规避。例如:

  1. Prothreads系统所有线程必须主动让出CPU的使用权。(非抢占式操作系)
  2. Prothreads的PT_BEGIN 到PT_END使用局部变量需要谨慎,局部变量赋值后,不能在引发任务调用的宏之后调用。
  3. Prothreads的线程里面如果使用switch ,case里面不能使用任务调用宏。
    但是如果我们使用RTOS和Prothreads进行对比,暂时抛开代码生态说,即使Prothreads具备卓越的低资源,当使用的舒适度完全比不过RTOS。例如Prothreads不具备最基础的OS的sleep的功能,不具备消息邮箱,信号量(超时机制),消息队列,低功耗支持,线程状态监控,事件机制等等,一些通用的RTOS同步接口。
    为了解决以上问题,我在原生的Prothreads上进行进一步的拓展,让其同样居于RTOS的丰富的同步接口,并且同时具备极低的系统资源开销。对于一些本来资源较低的芯片,在无法过多使用RTOS丰富的轮子,这将是一个更好的的操作系统。
    详情可见《第一章: Protothreads的拓展》后续为了方便描述,统一命名为PT_EX
    以下是我在github上进行开源的仓库地址:https://github.com/liufuzhao/Protothreads.git

附录

错误的switch使用方法
在这里插入图片描述
gcc -E 宏展开后
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值