技术系列之“状态机”

技术系列之“状态机”

作者: cppexplore  发布时间: 2015-09-13 11:03  阅读: 1194 次  推荐: 2                   原文链接   [收藏]

  一、状态机描述

  状态机理论最初的发展在数字电路设计领域。在数字电路方面,根据输出是否与输入信号有关,状态机可以划分为Mealy型和Moore型状态机;根据输出是否与输入信号同步,状态机可以划分为异步和同步状态机。而在软件设计领域,状态机设计的理论俨然已经自成一体。Moore型状态机的输出只和当前状态有关,和输入无关,如果在软件设计领域设计出这种类型的状态机,则该状态机接受的事件都是无内蕴信息的事件(输入)。Mealy型状态机的输入是由当前状态和输入共同决定,对应到软件设计领域,则该状态机接收的事件含有内蕴信息,并且影响状态机的输出。显然,这种划分在软件设计领域毫无意义。虽然软件设计领域的状态机也有同步和异步的划分,但和数字电路方面的同步异步已经不同。

  除了《数字电路》,涉及到状态机的课程就是《编译原理》了(本人属计算机专业,其它专业是否涉及到状态机就不清楚了)。下面简单回顾一下《编译原理》里有关有限状态机的描述。在编译原理课程里面,对有限状态机的描述仅限在编译领域,特定状态,针对输入字符,发生状态改变,没有额外的行为,另编译原理里有限状态机的构成要素,还包含唯一的初始状态和一个终态集。数学语言描述如下:一个有限状态机M是一个五元组,M=(K,E,T,S,Z)。其中(1)K是一个有穷集,其中的每个元素称为状态;(2)E是一个有穷字母表,它的每个元素称为一个输入字符;(3)T是转换函数,是K×E->K上的映射;(4)S是K中的元素,是唯一的一个初态;(5)Z是K的一个子集,是一个终态集,或者叫结束集。很明显,状态机在编译原理里的讲解已经特化,输入被定位为字符集,状态改变的时候没有额外动作发生。

  与编译原理中的状态机不同,软件设计领域中通用状态机的输入不是字符集,而是被称作事件的结构(可以是结构体,也可以是类对象),并且特定的状态下,针对发生的事件,不仅发生状态改变,而且产生动作。借鉴编译原理中状态机的初始状态和终态,通用状态机的数学语言描述如下:一个通用有限状态机M是一个七元组,M={K,E,T,M,F,S,Z}。其中(1)K是一个有穷集,其中的每个元素称为状态;(2)E是一个有穷集,它的每个元素称为一个事件;(3)T是转换函数,是K×E->K上的映射;(4)M是一个有穷集,它的每个元素称为动作;(5)F是动作映射函数,是K×E->M上的映射;(6)S是K中的元素,是唯一的一个初态;(7) Z是K的一个子集,是一个终态集,或者叫结束集。实用的状态机可以做进一步的优化,首先,可以把 (3)(5)整合在一起,做一个K×E->{K,M}的映射;其次从实用性的角度出发,禁止状态接收空事件(无输入的情况下,状态发生改变),作为弥补,为每个状态增加进入动作和离开动作;第三,鉴于定时器在系统中,尤其是在状态机中的重要性,可以为每个状态增加定时器以及超时后的状态转换。本文后面的讲述以及实现暂不考虑把定时器特化,如果需要,可以在状态的进入动作中初始化定时器(另:关于定时器,以后会写文章《系统设计之 定时器》)。

  二、状态机分类(后文中如无特别说明,则状态机指软件设计领域的通用有限状态机)

  依据状态之间是否有包含关系,分以下两种:

  (1)常规状态机。状态机中的所有状态是不相交的、互斥的。

  (2)层次状态机。状态机中的状态之间要么是互斥的,要么是真包含的,可以用树性结构来描述这些状态集,包含其它状态的状态称为枝节点,不包含其它状态的状态称为叶节点,为方便单树描述,总是设计一个状态包含所有的状态节点,称为根节点。状态机的状态只能停留在叶节点,而不能停留在枝节点,每个枝节点需要指定一个子节点为它的默认子节点,以便状态机进入枝节点的时候能够停留到叶节点。

  三、状态机实现

  • switch/case if/else方式实现。用于少量状态(3个及其以下)的时候,不需要引入专门的状态机模块。这种方式不能编写通用的状态机模块,不再多说。
  • 面向过程方式:宏是实现面向过程方式的通用方式。虽然在状态机层面还是可以用面向对象的方式封装,这里还是把它称为面向过程的方式。

  1. 常规状态机模块实现。这个状态机涉及到结构由上而下为:

  • 顶层结构是状态机:当前状态id,缺省操作,状态表
  • 状态表:状态数组
  • 状态结构:状态id,状态名,进入操作,退出操作,缺省操作,状态事件表(数组)
  • 状态事件结构:操作,事件,下一状态的id
  • 状态机的算法是由状态机的结构决定的

  实现如下:

#define SINGLE_STATE_MAX_EVENT 10
typedef int FSM_EVENT_ID;
typedef struct event_param_st
{
    FSM_EVENT_ID id;
    union{
        int i;
    }data;
}FSM_EVENT;
typedef int FSM_STATE_ID;
typedef void (*FSM_FUNC)(FSM_EVENT *);
typedef struct state_event_st
{
    FSM_FUNC func;
    FSM_EVENT_ID event;
    FSM_STATE_ID state;
}FSM_STATE_EVENT;
typedef struct state_st
{
    FSM_STATE_ID id;
    char *name;
    FSM_FUNC enter_func;
    FSM_FUNC exit_func;
    FSM_FUNC default_func;
    FSM_STATE_EVENT event_table[SINGLE_STATE_MAX_EVENT];
}FSM_STATE;
typedef FSM_STATE STATE_TABLE[];
typedef FSM_STATE * PTR_STATE_TABLE;
#define END_EVENT_ID -1
#define END_STATE_ID -1
#define BEGIN_FSM_STATE_TABLE(state_stable) static STATE_TABLE state_stable={
#define BEGIN_STATE(id,name,enter_func,exit_func,default_func) {id,name,enter_func,exit_func,default_func,{
#define STATE_EVENT_ITEM(func,event,state) {func,event,state},
#define END_STATE(id) {NULL,END_EVENT_ID,END_STATE_ID}}},
#define END_FSM_STATE_TABLE(state_stable) {END_STATE_ID,NULL,NULL,NULL,NULL,NULL}};
typedef struct fsm_st
{
    FSM_STATE_ID state_id;
    FSM_FUNC default_func;
    PTR_STATE_TABLE state_tables;
}FSM;
void fsm_do_event(FSM &fsm, FSM_EVENT &event)
{
    FSM_STATE *state=&(fsm.state_tables[fsm.state_id]);
    int i=0;
    while(state->event_table[i].event!=END_EVENT_ID)
    {
        if(state->event_table[i].event==event.id)
            break;
        i++;
    }
    if(state->event_table[i].event!=END_EVENT_ID)
    {
        if(state->id!=state->event_table[i].state)
        {
            if(state->exit_func )
                state->exit_func(&event);
        }
        if(state->event_table[i].func)
            state->event_table[i].func(&event);
        if(state->id!=state->event_table[i].state)
        {
            if(fsm.state_tables[state->event_table[i].state].enter_func)
                fsm.state_tables[state->event_table[i].state].enter_func(&event);
            fsm.state_id=state->event_table[i].state;
        }
    }
    else
    {
        if(state->default_func)
            state->default_func(&event);
        else
        {
            if(fsm.default_func)
                fsm.default_func(&event);
        }
    }
}

  以上说明实现原理,有特殊需要的话可以自己定制状态机,比如上面的状态事件表数组的上限取的是单个状态中事件项的最大值,也可以定义为所有事件的个数,这样的话事件也不需要查询,可以象状态样直接定位,只是状态事件表会浪费一些存储空间。上面的FSM_EVENT仅仅是个例子,实际开发根据需要定义不同的union。上面的算法也是假定状态表的状态定义是从0开始,顺序递增的。

  对外部调用而言,最后的状态机结构和事件执行的方法可以封装为对象。下面举例说明状态机的定义(事件和状态都应该是enum类型,这里直接使用数字,仅为说明问题而已)。

BEGIN_FSM_STATE_TABLE(my_state_table)
    BEGIN_STATE(0,"first",enter_fsm,exit_fsm,defualt_fsm)
        STATE_EVENT_ITEM(func_fsm,1,1)
        STATE_EVENT_ITEM(func_fsm,2,2)
    END_STATE(0)
    BEGIN_STATE(1,"second",enter_fsm,exit_fsm,defualt_fsm)
        STATE_EVENT_ITEM(func_fsm,1,2)
        STATE_EVENT_ITEM(func_fsm,2,0)
    END_STATE(1)
    BEGIN_STATE(2,"third",enter_fsm,exit_fsm,defualt_fsm)
        STATE_EVENT_ITEM(func_fsm,1,0)
        STATE_EVENT_ITEM(func_fsm,2,1)
    END_STATE(2)
END_FSM_STATE_TABLE(my_state_table)
void enter_fsm(FSM_EVENT * event)
{
    printf("enter me\n");
}
void exit_fsm(FSM_EVENT * event)
{
    printf("exit me\n");
}
void defualt_fsm(FSM_EVENT * event)
{
    printf("i am defualt_fsm\n");
}
void func_fsm(FSM_EVENT * event)
{
    printf("i am func_fsm\n");
}
int main()
{
    printf("i am main\n");
    FSM fsm={0,defualt_fsm,my_state_table};
    printf("state[%d],name[%s]\n",fsm.state_id,fsm.state_tables[fsm.state_id].name);
    FSM_EVENT event;
    event.id=1;
    event.data.i=1;
    fsm_do_event(fsm,event);
    printf("state[%d],name[%s]\n",fsm.state_id,fsm.state_tables[fsm.state_id].name);
}
时间: 2024-10-10 09:16:13

技术系列之“状态机”的相关文章

Azure Messaging-ServiceBus Messaging消息队列技术系列6-消息回执

上篇博文中我们介绍了Azure Messaging的重复消息机制.At most once 和At least once. Azure Messaging-ServiceBus Messaging消息队列技术系列5-重复消息:at-least-once at-most-once 本文中我们主要研究并介绍Azure Messaging的消息回执机制:实际应用场景: 同步收发场景下,消息生产者和消费者双向应答模式,例如:张三写封信送到邮局中转站,然后李四从中转站获得信,然后在写一份回执信,放到中转站

10.Java 加解密技术系列之 DH

Java 加解密技术系列之 DH 序 概念 原理 代码实现 结果 结束语 序 上一篇文章中简单的介绍了一种非对称加密算法 — — RSA,今天这篇文章,继续介绍另一种非对称加密算法 — — DH.当然,可能有很多人对这种加密算法并不是很熟悉,不过没关系,希望今天这篇文章能帮助你熟悉他. 原理 整个通信过程中g.g^a.g^b是公开的,但由于g.a.b都是整数,通过g和g^a得到a还是比较容易的,b也是如此,所以最终的“密钥”g^(a*b)还是可以被计算出来的.所以实际的过程还需要在基本原理上加入

8.Java 加解密技术系列之 PBE

Java 加解密技术系列之 PBE 序 概念 原理 代码实现 结束语 序 前 边的几篇文章,已经讲了几个对称加密的算法了,今天这篇文章再介绍最后一种对称加密算法 — — PBE,这种加密算法,对我的认知来说,并没有 DES.3DES.AES 那么流行,也不尽然,其实是我之前并没有这方面的需求,当然接触他的机会也就很少了,因此,可想而知,没听过显然在正常不过了. 概念 PBE,全称为“Password Base Encryption”,中文名“基于口令加密”,是一种基于密码的加密算法,其特点是使用

11.Java 加解密技术系列之 总结

Java 加解密技术系列之 总结 序 背景 分类 常用算法 原理 关于代码 结束语 序 上一篇文章中简单的介绍了第二种非对称加密算法 — — DH,这种算法也经常被叫做密钥交换协议,它主要是针对密钥的保护.同时,由于水平的限制,打算这个系列就到此为止了,这篇文章就算是一个总结吧,回顾一下这几个月来都写了些什么. 背景 其 实,在开始写这个系列之前,我对于 Java 的加解密也并不是那么了解.之所以要写这些文章,还主要是由于工作的原因.记得几个月以前,当时项目要做一个数字证书,证书的生成.存储.传

Entity Framework技术系列之8:使用Entity Framework技术实现RBAC模型

小分享:我有几张阿里云优惠券,用券购买或者升级阿里云相应产品最多可以优惠五折!领券地址:https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCode=ohmepe03 前言 RBAC(Role-Based Access Control,基于角色的访问控制),是继DAC(Discretionary Access Control,自主访问控制)和MAC(Mandatory Access Control,强制访问控

6. Java 加解密技术系列之 3DES

Java 加解密技术系列之 3DES 序 背景 概念 原理 代码实现 结束语 序 上一篇文章讲的是对称加密算法 — — DES,这篇文章打算在 DES 的基础上,继续多讲一点,也就是 3 重 DES — — Triple DES. 背景 至于 3DES 为什么会出现呢?其实,这个不难想到.由于 DES 是一种非常简便的加密算法,但是密钥长度比较短,计算量比较小,相对来说,比较容易被破解.因此,在 DES 的基础上,使用三重数据加密算法,对数据进行加密,这样来说,破解的概率就小了很多. 概念 3D

9.Java 加解密技术系列之 RSA

Java 加解密技术系列之 RSA 序 概念 工作流程 RSA 代码实现 加解密结果 结束语 序 距 离上一次写博客感觉已经很长时间了,先吐槽一下,这个月以来,公司一直在加班,又是发版.上线,又是新项目太紧,具体的就不多说了,想听我吐槽的小伙伴, 可以私信给我(*^__^*) .上一篇文章,已经把对称加密的算法讲完了.从今天开始,要说说非对称加密了.因为,非对称加密真的是太重要了,我们的日常生活中,都离不开非对称加密. 概念 在说 RSA 之前,首先聊聊什么是非对称加密.在讲对称加密的时候,就曾

5.Java 加解密技术系列之 DES

Java 加解密技术系列之 DES 序 背景 概念 基本原理 主要流程 分组模式 代码实现 结束语 序 前 几篇文章讲的都是单向加密算法,其中涉及到了 BASE64.MD5.SHA.HMAC 等几个比较常见的加解密算法.这篇文章,以及后面几篇,打算介绍几个对称加密算法,比如:DES.3DES(TripleDES).AES 等.那么,这篇文章主要是对 DES 大概讲一下. 背景 在 讨论 DES 之前,首先了解一下什么是对称加密算法吧.对于对称加密算法,他应用的时间比较早,技术相对来说比较成熟,在

7.java 加解密技术系列之 AES

java 加解密技术系列之 AES 序 概念 原理 应用 代码实现 结束语 序 这篇文章继续介绍对称加密算法,至于今天的主角,不用说,也是个厉害的角色 — — AES.AES 的出现,就是为了来替代原先的 DES 标准.现在来说,AES 的用途还是非常广泛的. 概念 AES, 全称为“Advanced Encryption Standard”,中文名“高级加密标准”,在密码学中又称 Rijndael 加密法,是美国联邦政府采用的一种区块加密标准.AES 加密算法作为新一代的数据加密标准汇聚了强安