有限状态机FSM思想广泛应用于硬件控制电路设计,也是软件上常用的一种处理方法(软件上称为FMM--有限消息机)。它把
复杂的控制逻辑分解成有限个稳定状态,在每个状态上判断事件,变连续处理为离散数字处理,符合计算机的工作特点。同
时,因为有限状态机具有有限个状态,所以可以在实际的工程上实现。但这并不意味着其只能进行有限次的处理,相反,有限
状态机是闭环系统,有限无穷,可以用有限的状态,处理无穷的事务。
    有限状态机的工作原理如图1所示,发生事件(event)后,根据当前状态(cur_state),决定执行的动作(action),并设置
下一个状态号(nxt_state)。                          -------------
                         |           |-------->执行动作action
     发生事件event ----->| cur_state |
                         |           |-------->设置下一状态号nxt_state
                         -------------
                            当前状态
                      图1 有限状态机工作原理                                e0/a0
                              --->--
                              |    |
                   -------->----------
             e0/a0 |        |   S0   |-----
                   |    -<------------    | e1/a1
                   |    | e2/a2           V
                 ----------           ----------
                 |   S2   |-----<-----|   S1   |
                 ----------   e2/a2   ----------
                       图2 一个有限状态机实例               --------------------------------------------
              当前状态   s0        s1        s2     | 事件
              --------------------------------------------
                       a0/s0      --       a0/s0   |  e0
              --------------------------------------------
                       a1/s1      --        --     |  e1
              --------------------------------------------
                       a2/s2     a2/s2      --     |  e2
              --------------------------------------------                表1 图2状态机实例的二维表格表示(动作/下一状态)     图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;
            }
            break;
        case s1:                        //在s1状态
            if(e2_event){               //如果发生e2事件,那么就执行a2动作,并将状态转移到s2态;
                执行a2动作;
                nxt_state = s2;
            }
            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,有三
种类型的事件:上层下达的命令事件;下层到达的标志和数据的收包事件;超时定时器超时事件。
    
                    上层命令(open,close)事件
            -----------------------------------
                    --------------------
                    |       TCP        |  <----------超时事件timeout
                    --------------------
            -----------------------------------
                 RST/SYN/FIN/ACK/DATA等收包事件
                    
                    图3 三大类TCP状态机事件     由图3可知,此TCP协议栈采用横着写方式实现,有3种事件处理函数,上层命令处理函数(如tcp_close);超时事件处理函
数 (tmr_slow);下层收包事件处理函数(tcp_process)。值得一提的是,在收包事件函数里,在各个状态里判断
RST/SYN/FIN/ACK/DATA等标志(这些标志类似于事件),看起来象竖着写方式,其实,如果把包头和数据看成一个整体,那么,
RST/SYN/FIN/ACK/DATA等标志就不必被看成独立的事件,而是属于同一个收包事件里的细节,这样,就不会认为在状态里查找
事件,而是总体上看,是在收包事件里查找状态(横着写)。
    
    在PPP里更是到处都能见到横着写的现象,有时间的话再细说。我个人感觉在实现PPP框架协议前必须了解横竖两种写法,
而且只有使用横着写的方式才能比较完美地实现PPP

转自CSDN,关于状态机的更多相关文章

  1. osip状态机分析

    转载于:http://blog.csdn.net/lbc2100/article/details/48342889 OSIP的核心是系统状态机,在不同情况下,系统处于不同的状态,在某一状态下当系统发生 ...

  2. AI 状态机

    by AKara 2010-11-11 @ http://blog.csdn.net/akara @ akarachen(at)gmail.com @weibo.com/akaras 一个简单横版游戏 ...

  3. 状态机的c语言编程

    http://blog.csdn.net/shandongdaya/article/details/7282547 一 有限状态机的实现方式 有限状态机(Finite State Machine或者F ...

  4. 转载 java枚举类型enum的使用 (原文地址:http://blog.csdn.net/wgw335363240/article/details/6359614)

    java枚举类型enum的使用 最近跟同事讨论问题的时候,突然同事提到我们为什么java中定义的常量值不采用enmu枚举类型,而采用public final static 类型来定义呢?以前我们都是采 ...

  5. 【Android 多媒体开发】 MediaPlayer 状态机 接口 方法 解析

    作者 : 韩曙亮 转载请著名出处 :  http://blog.csdn.net/shulianghan/article/details/38487967 一. MediaPlayer 状态机 介绍 ...

  6. Qt 状态机框架学习(没学会)

    Qt状态机框架是基于状态图XML(SCXML) 实现的.从Qt4.6开始,它已经是QtCore模块的一部分.尽管它本身是蛮复杂的一套东西,但经过和Qt的事件系统(event system).信号槽(s ...

  7. Qt状态机框架

    The State Machine Framework 状态机框架提供了用于创建和执行状态图的类.概念和符号是基于Harel的Statecharts: A visual formalism for c ...

  8. 普通的年轻状态机,纯C语言

    我们第一次接触到了状态机.在数字电路课程.计数器.串行奇偶校验.考了1连续报错电路 等待,两者都需要一个状态机模型.电路实现这些功能,与状态机的状态转移图.状态转移表是等价. 后.然后,我们联系了状态 ...

  9. 状态机编程思想(2):删除代码注释(目前支持C/C++和Java)

    有时为了信息保密或是单纯阅读代码,我们常常需要删除注释. 之前考虑过正则表达式,但是感觉实现起来相当麻烦.而状态机可以把多种情况归为一类状态再行分解,大大简化问题.本文就是基于状态机实现的. 删除C/ ...

随机推荐

  1. eclipse 在写XML时 包类名自动提醒的问题

    需要加一个STS插件 配置很简单 参考了  https://blog.csdn.net/HH775313602/article/details/70176531 在 https://spring.io ...

  2. 清理数据库事务——SQL语句

    清除流程内部的所有相关数据 eg1: declare @procedureTemp table ( [ProcedureCode] varchar(10) ) declare @ProcedureCo ...

  3. iOS Crash

    常见原因及解决方法: 1. 访问数组类对象越界或插入了空对象NSMutableArray/NSMutableDictionary/NSMutableSet 等类下标越界,或者 insert 了一个 n ...

  4. RN笔记

    https://facebook.github.io/react-native/docs/using-a-listview.html react native类似于react,不过它使用的是原生组件, ...

  5. python基础知识13-迭代器与生成器,导入模块

    异常处理作业讲解 file = open('/home/pyvip/aaa.txt','w+') try: my_dict = {'name':'adb'} file.write(my_dict['a ...

  6. Python基础:字典(dict)与集合(set)

    查找场景下与列表的性能对比 字典与集合之所以高效的原因是:内部结构都是一张哈希表. 平均情况下插入.查找和删除的时间复杂度为 O(1). 假设有数量100,000的产品列表: import time ...

  7. lucene segment的产生,flush, commit与es的refresh,flush

    1 segment的产生 当索引一个文档时,如果存在空闲的segment(未被其他线程锁定),则取出空闲segment list中的最后一个segment(LIFO),并锁定,将文档索引至该segme ...

  8. PHP优化之批量操作MySQL

    设计一个数据表如下: create table optimization( id INT NOT NULL AUTO_INCREMENT, value VARCHAR(10) NOT NULL, PR ...

  9. [jenkins学习篇] 安装jenkins

    1 下载war包,https://jenkins.io/download/ 2 安装jar包,java -jar jenkins.war 3 打开网址:http://localhost:8080 4 ...

  10. [android开发篇] 权限

    Android 应用采用 Java 编程语言编写.Android SDK 工具将您的代码 — 连同任何数据和资源文件 — 编译到一个 APK:Android 软件包,即带有 .apk 后缀的存档文件中 ...