状态机的两种写法

2023-05-16

http://kb.cnblogs.com/page/528972/

状态机的两种写法

http://kb.cnblogs.com/page/528966/       

技术系列之“状态机”


http://kb.cnblogs.com/page/527841/

如何建设全功能团队

http://kb.cnblogs.com/page/519485/

软件设计杂谈


有限状态机FSM思想广泛应用于硬件控制电路设计,也是软件上常用的一种处理方法(软件上称为FMM有限消息机)。它把复杂的控制逻辑分解成有限个稳定状态,在每个状态上判断事件,变连续处理为离散数字处理,符合计算机的工作特点。同时,因为有限状态机具有有限个状态,所以可以在实际的工程上实现。但这并不意味着其只能进行有限次的处理,相反,有限状态机是闭环系统,有限无穷,可以用有限的状态,处理无穷的事务。

  有限状态机的工作原理如图1所示,发生事件(event)后,根据当前状态(cur_state) ,决定执行的动作(action),并设置下一个状态号(nxt_state)。

  图2为一个状态机实例的状态转移图,它的含义是:

  • 在s0状态,如果发生e0事件,那么就执行a0动作,并保持状态不变;
  • 如果发生e1事件,那么就执行a1动作,并将状态转移到s1态;
  • 如果发生e2事件,那么就执行a2动作,并将状态转移到s2态;
  • 在s1状态,如果发生e2事件,那么就执行a2动作,并将状态转移到s2态;
  • 在s2状态,如果发生e0事件,那么就执行a0动作,并将状态转移到s0态。

  有限状态机不仅能够用状态转移图表示,还可以用二维的表格代表。一般将当前状态号写在横行上,将事件写在纵列上,如表1所示。其中“--”表示空(不执行动作,也不进行状态转移),“an/sn”表示执行动作an,同时将下一状态设置为sn。表1和图2表示的含义是完全相同的。

  观察表1可知,状态机可以用两种方法实现:竖着写(在状态中判断事件)和横着写(在事件中判断状态)。这两种实现在本质上是完全等效的,但在实际操作中,效果却截然不同。

  竖着写(在状态中判断事件)C代码片段:


cur_state = nxt_state;   
switch(cur_state) //在当前状态中判断事件
{            
    case s0: //在s0状态   
        if(e0_event) //如果发生e0事件,那么就执行a0动作,并保持状态不变;
        {   
        //执行a0动作;               
        //nxt_state = s0;  //因为状态号是自身,所以可以删除此句,以提高运行速度。
        } 
        else if(e1_event) //如果发生e1事件,那么就执行a1动作,并将状态转移到s1态;
        {   
            //执行a1动作;
            nxt_state = s1;
        }           
        else if(e2_event) //如果发生e2事件,那么就执行a2动作,并将状态转移到s2态;
        {  
            //执行a2动作;
            nxt_state = s2;
        }
        else
        {
            break;    
        }   
    case s1: //在s1状态
        if(e2_event) //如果发生e2事件,那么就执行a2动作,并将状态转移到s2态; 
        {                
            //执行a2动作;
         nxt_state = s2;
        }           
        else
        {
      break;
        }
    case s2: //在s2状态
        if(e0_event)  //如果发生e0事件,那么就执行a0动作,并将状态转移到s0态;
        {
            //执行a0动作;               
            nxt_state = s0;
        }
}  

  横着写(在事件中判断状态)C代码片段:


//e0事件发生时,执行的函数
void e0_event_function(int * nxt_state)
{   
    int cur_state;   
    cur_state = *nxt_state;   
    switch(cur_state)
    {       
        case s0: //观察表1,在e0事件发生时,s1处为空   
        case s2: //执行a0动作;           
        *nxt_state = s0;
    }
}
//e1事件发生时,执行的函数
void e1_event_function(int * nxt_state)
{   
    int cur_state;   
    cur_state = *nxt_state;   
    switch(cur_state)
    {       
        case s0: //观察表1,在e1事件发生时,s1和s2处为空           
            //执行a1动作;           
            *nxt_state = s1;
    }
}
//e2事件发生时,执行的函数
void e2_event_function(int * nxt_state)
{   
    int cur_state;   
    cur_state = *nxt_state;   
    switch(cur_state)
    {       
        case s0: //观察表1,在e2事件发生时,s2处为空       
        case s1:           
            //执行a2动作;           
            *nxt_state = s2; 
    }
}  

  上面横竖两种写法的代码片段,实现的功能完全相同,但是,横着写的效果明显好于竖着写的效果。理由如下:

  1、竖着写隐含了优先级排序(其实各个事件是同优先级的),排在前面的事件判断将毫无疑问地优先于排在后面的事件判断。这种if/else if写法上的限制将破坏事件间原有的关系。而横着写不存在此问题。

  2、由于处在每个状态时的事件数目不一致,而且事件发生的时间是随机的,无法预先确定,导致竖着写沦落为顺序查询方式,结构上的缺陷使得大量时间被浪费。对于横着写,在某个时间点,状态是唯一确定的,在事件里查找状态只要使用switch语句,能一步定位到相应的状态,延迟时间可以预先准确估算。而且在事件发生时,调用事件函数,在函数里查找唯一确定的状态,并根据其执行动作和状态转移的思路清晰简洁, 效率高,富有美感。

  总之,我个人认为,在软件里写状态机,使用横着写的方法比较妥帖。

  竖着写的方法也不是完全不能使用,在一些小项目里,逻辑不太复杂,功能精简,同时为了节约内存耗费,竖着写的方法也不失为一种合适的选择。

  在FPGA类硬件设计中,以状态为中心实现控制电路状态机(竖着写)似乎是唯一的选择,因为硬件不太可能靠事件驱动(横着写)。不过,在FPGA里有一个全局时钟,在每次上升沿时进行状态切换,使得竖着写的效率并不低。虽然在硬件里竖着写也要使用IF/ELSIF这类查询语句(用VHDL开发),但他们映射到硬件上是组合逻辑,查询只会引起门级延迟(ns量级),而且硬件是真正并行工作的,这样竖着写在硬件里就没有负面影响。因此,在硬件设计里,使用竖着写的方式成为必然的选择。这也是为什么很多搞硬件的工程师在设计软件状态机时下意识地只使用竖着写方式的原因,盖思维定势使然也。

  TCP和PPP框架协议里都使用了有限状态机,这类软件状态机最好使用横着写的方式实现。以某TCP协议为例,见图3,有三种类型的事件:上层下达的命令事件;下层到达的标志和数据的收包事件;超时定时器超时事件。

  状态机的两种写法 - loving you - 天道酬勤

  图3可知,此TCP协议栈采用横着写方式实现,有3种事件处理函数,上层命令处理函数(如tcp_close);超时事件处理函数(tmr_slow);下层收包事件处理函数(tcp_process)。值得一提的是,在收包事件函数里,在各个状态里判断RST/SYN/FIN/ACK/DATA等标志(这些标志类似于事件),看起来象竖着写方式,其实,如果把包头和数据看成一个整体,那么,RST/SYN/FIN/ACK/DATA等标志就不必被看成独立的事件,而是属于同一个收包事件里的细节,这样,就不会认为在状态里查找事件,而是总体上看,是在收包事件里查找状态(横着写)。

  在PPP里更是到处都能见到横着写的现象,有时间的话再细说。我个人感觉在实现PPP框架协议前必须了解横竖两种写法,而且只有使用横着写的方式才能比较完美地实现PPP。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

状态机的两种写法 的相关文章

  • ubuntu 下 .7z 文件解压时,子文件夹内的内容被解压到根文件夹问题

    7z e log4cplus 2 0 8 7z o home wukai Documents log4cplus 参数使用 e 时 xff0c 会导致子文件夹内的内容被解压到根文件夹 xff0c 导致子文件夹没东西 xff0c 且覆盖了根文
  • configure: error: cannot find sources (src/logger.cxx) in . or ..

    配置的时候 xff0c 找不到文件 查看下src文件夹下是不是没有这个文件 xff0c 如果没有 xff0c 可能性有一下两个 1 解压的时候出错 xff0c 导致此文件被解压到其他文件夹 xff1b xff08 参考https mp cs
  • windows下python下载及安装

    下载python安装包 进入python官网 xff1a https www python org 鼠标移动到 Downloads gt 34 Windows 34 上 xff0c 可以看到最新版本是3 11 3版本 点击 Windows
  • 修改环境变量

    点击 windows 按钮 xff0c 输入 环境 xff0c 点击右侧的 编辑系统环境变量 点击 环境变量 按钮 按如下顺序将python添加到环境变量中 然后再把所有弹框的确定按钮都点下
  • windows下创建python文件

    1 打开python IDLE 按下 windows 按钮 xff0c 输入python xff0c 单击 IDLE Python 3 9 64 bit 点击File gt New File 新文件未命令 xff0c 内容空 随意编辑代码
  • python代码注释

    在python中 xff0c 存在三种类型的注释 xff1a 单行注释 多行注释和中文声明注释 1 单行注释 xff08 在需要注释的内容前面加 xff09 注释内容 2 多行注释 xff08 将要注释的内容包含在 或者 内 xff09 3
  • python3.9.13 IDLE设置缩进值

    Options gt 34 Configure IDLE 34 gt 34 Windows 34 Indent spaces 即是缩进值
  • unindent does not match any outer indentation level

    python运行时 xff0c 报错 unindent does not match any outer indentation level 有某行的缩进和其他行不匹配
  • python分行

    方式一 xff1a print 34 123 34 34 456 34 方式二 xff1a print 34 wer asd 34 输出 123456 werasd
  • python命名规范

    1 模块名 xff1a 尽量短小 xff0c 全部小写 xff0c 可以使用下划线分隔多个字母 如 xff1a func 1 func 2 2 类名 xff1a 采用单词首字母大写的方式 如 xff1a Student Teacher 3
  • YOLOv5之autoanchor看这一篇就够了

    简单粗暴 xff0c 废话也不罗嗦了 xff0c 学习目的就是解决下面三个问题 xff0c 1 默认anchor t设置为4 xff0c 这个参数如何调整 xff1f 有没有必要调整 xff1f xff08 首先网上很多说这个参数是长宽比是
  • 反转一个长字符串中的子字符串

    第一次练习写博客 xff0c 记录下自己感觉满意的成果吧 include lt stdio h gt include lt string h gt bool findPosition char sur char dst int amp st
  • c中全局变量,全局结构体使用

    1 在A 中定义的函数 xff0c 如何在 B 中调用 xff1f 如果有头文件 xff0c 在头文件中声明 xff0c 在B 文件中 include 头文件就可以了 如果是在 c 文件中声明的 xff0c 在 B 中加 extent 声明
  • Vue3展示Markdown内容

    起因 想要搭建一个个人网站 xff0c 然后在网站上展示个人信息以及平时学习或者使用框架的一些内容 所以需要一个能够将markdown内容转化到页面上展示 xff08 就类似于github或者各大博客网站 xff09 个人网站是vue3 x
  • debian linux 添加永久环境变量

    写在前面的话 搜索linux添加环境变量 xff0c 网上已经有很多的教程 xff0c 本来就几个命令还是把我搞的好惨 xff0c 几个坑大牛们不指出来 xff0c 我这小白就卡在里面了 xff0c 写下血泪史供参考 关于环境变量 expo
  • CLion、IDEA、Pycharm等用WSL访问不了环境变量的解决方案——更新于2021.12

    目录 相关文献PowerShell解决方案 博主全网搜索过很多教程 xff0c 各种碰壁不成功 xff0c 最终使用了PowerShell成功的 本文将介绍PowerShell的成功方法和几个替代方案 博主使用WSL是Ubuntu 20 0
  • Linux下安装xrdp实现远程桌面

    使用rdp协议访问远程Linux桌面 一般情况下 xff0c 如果需要登陆远程Linux系统 xff0c 我们会使用ssh telnet来完成 xff0c 如果需要登陆到远程Linux系统的桌面环境 xff0c 我们可能会使用VNC VNC
  • 树莓派——xrdp win10远程登录以及蓝屏问题

    1 安装xrdp 使用Putty命令行输入以下命令 sudo apt get install xrdp sudo apt get install tightvncserver xrdp 安装完成后 xff0c 重启xrdp服务器 sudo
  • 使用lnmp安装Nextcloud出现404问题解决方法

    最新消息 特大消息特大消息 xff0c 由于答主解决不了后续出现的WEBDAV接口错误问题 xff0c 因此更改了安装方式 61 61 61 61 61 DOCKER xff01 装完之后感慨一下 xff0c docker大法真好 参考教程
  • 笔记:1. Centos 安装 mpicc

    心情 xff1a 历时一年 xff0c 考上了研究生 xff0c 从此踏上第一性原理计算的道路 是有点小开心 xff0c 因为以后可以做自己喜欢的事情 xff0c 剩下的就是怎么通过做自己喜欢的事挣点钱 xff0c 养活自己 正文 目的 x

随机推荐