侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

Linux有限状态机FSM的理解与实现

linux  /  管理员 发布于 7年前   172

有限状态机(finite state machine)简称FSM,表示有限个状态及在这些状态之间的转移和动作等行为的数学模型,在计算机领域有着广泛的应用。FSM是一种逻辑单元内部的一种高效编程方法,在服务器编程中,服务器可以根据不同状态或者消息类型进行相应的处理逻辑,使得程序逻辑清晰易懂。

那有限状态机通常在什么地方被用到?

处理程序语言或者自然语言的 tokenizer,自底向上解析语法的parser,
各种通信协议发送方和接受方传递数据对消息处理,游戏AI等都有应用场景。

状态机有以下几种实现方法,我将一一阐述它们的优缺点。

一、使用if/else if语句实现的FSM
使用if/else if语句是实现的FSM最简单最易懂的方法,我们只需要通过大量的if /else if语句来判断状态值来执行相应的逻辑处理。

看看下面的例子,我们使用了大量的if/else if语句实现了一个简单的状态机,做到了根据状态的不同执行相应的操作,并且实现了状态的跳转。

//比如我们定义了小明一天的状态如下enum{  GET_UP,  GO_TO_SCHOOL,  HAVE_LUNCH,  GO_HOME,  DO_HOMEWORK,  SLEEP,};int main(){  int state = GET_UP;  //小明的一天  while (1)  {    if (state == GET_UP)    {      GetUp(); //具体调用的函数      state = GO_TO_SCHOOL; //状态的转移    }    else if (state == GO_TO_SCHOOL)    {      Go2School();      state = HAVE_LUNCH;    }    else if (state == HAVE_LUNCH)    {      HaveLunch();    }    ...    else if (state == SLEEP)    {      Go2Bed();      state = GET_UP;    }  }  return 0;}

看完上面的例子,大家有什么感受?是不是感觉程序虽然简单易懂,但是使用了大量的if判断语句,使得代码很低端,同时代码膨胀的比较厉害。这个状态机的状态仅有几个,代码膨胀并不明显,但是如果我们需要处理的状态有数十个的话,该状态机的代码就不好读了。

二、使用switch实现FSM

使用switch语句实现的FSM的结构变得更为清晰了,其缺点也是明显的:这种设计方法虽然简单,通过一大堆判断来处理,适合小规模的状态切换流程,但如果规模扩大难以扩展和维护。

int main(){  int state = GET_UP;  //小明的一天  while (1)  {    switch(state)    {    case GET_UP:      GetUp(); //具体调用的函数      state = GO_TO_SCHOOL; //状态的转移      break;    case GO_TO_SCHOOL:      Go2School();      state = HAVE_LUNCH;      break;    case HAVE_LUNCH:      HaveLunch();      state = GO_HOME;      break;      ...    default:      break;    }  }  return 0;}

三、使用函数指针实现FSM

使用函数指针实现FSM的思路:建立相应的状态表和动作查询表,根据状态表、事件、动作表定位相应的动作处理函数,执行完成后再进行状态的切换。

当然使用函数指针实现的FSM的过程还是比较费时费力,但是这一切都是值得的,因为当你的程序规模大时候,基于这种表结构的状态机,维护程序起来也是得心应手。

下面给出一个使用函数指针实现的FSM的框架:

我们还是以“小明的一天”为例设计出该FSM。

先给出该FSM的状态转移图:

下面讲解关键部分代码实现

首先我们定义出小明一天的活动状态

//比如我们定义了小明一天的状态如下enum{  GET_UP,  GO_TO_SCHOOL,  HAVE_LUNCH,  DO_HOMEWORK,  SLEEP,};

我们也定义出会发生的事件

enum{  EVENT1 = 1,  EVENT2,  EVENT3,};

定义状态表的数据结构

typedef struct FsmTable_s{  int event;  //事件  int CurState; //当前状态  void (*eventActFun)(); //函数指针  int NextState; //下一个状态}FsmTable_t;

接下来定义出最重要FSM的状态表,我们整个FSM就是根据这个定义好的表来运转的。

FsmTable_t XiaoMingTable[] ={  //{到来的事件,当前的状态,将要要执行的函数,下一个状态}  { EVENT1, SLEEP,      GetUp,    GET_UP },  { EVENT2, GET_UP,     Go2School,  GO_TO_SCHOOL },  { EVENT3, GO_TO_SCHOOL,  HaveLunch,  HAVE_LUNCH },  { EVENT1, HAVE_LUNCH,   DoHomework,  DO_HOMEWORK },  { EVENT2, DO_HOMEWORK,   Go2Bed,    SLEEP },  //add your codes here};

状态机的注册、状态转移、事件处理的动作实现

/*状态机注册*/void FSM_Regist(FSM_t* pFsm, FsmTable_t* pTable){  pFsm->FsmTable = pTable;}/*状态迁移*/void FSM_StateTransfer(FSM_t* pFsm, int state){  pFsm->curState = state;}/*事件处理*/void FSM_EventHandle(FSM_t* pFsm, int event){  FsmTable_t* pActTable = pFsm->FsmTable;  void (*eventActFun)() = NULL; //函数指针初始化为空  int NextState;  int CurState = pFsm->curState;  int flag = 0; //标识是否满足条件  int i;  /*获取当前动作函数*/  for (i = 0; i<g_max_num; i++)  {    //当且仅当当前状态下来个指定的事件,我才执行它    if (event == pActTable[i].event && CurState == pActTable[i].CurState)    {      flag = 1;      eventActFun = pActTable[i].eventActFun;      NextState = pActTable[i].NextState;      break;    }  }  if (flag) //如果满足条件了  {    /*动作执行*/    if (eventActFun)    {      eventActFun();    }    //跳转到下一个状态    FSM_StateTransfer(pFsm, NextState);  }  else  {    // do nothing  }}

主函数我们这样写,然后观察状态机的运转情况

int main(){  FSM_t fsm;  InitFsm(&fsm);  int event = EVENT1;   //小明的一天,周而复始的一天又一天,进行着相同的活动  while (1)  {    printf("event %d is coming...\n", event);    FSM_EventHandle(&fsm, event);    printf("fsm current state %d\n", fsm.curState);    test(&event);     sleep(1); //休眠1秒,方便观察  }  return 0;}

看一看该状态机跑起来的状态转移情况:

上面的图可以看出,当且仅当在指定的状态下来了指定的事件才会发生函数的执行以及状态的转移,否则不会发生状态的跳转。这种机制使得这个状态机不停地自动运转,有条不絮地完成任务。

与前两种方法相比,使用函数指针实现FSM能很好用于大规模的切换流程,只要我们实现搭好了FSM框架,以后进行扩展就很简单了(只要在状态表里加一行来写入新的状态处理就可以了)。

需要FSM完整代码的童鞋请访问我的github

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


  • 上一条:
    Linux编程之ICMP洪水攻击
    下一条:
    Linux下安装MySQL8.0.11的教程
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在Linux系统中使用Iptables实现流量转发功能流程步骤(0个评论)
    • vim学习笔记-入门级需要了解的一些快捷键(0个评论)
    • 在centos7系统中实现分区并格式化挂载一块硬盘到/data目录流程步骤(0个评论)
    • 在Linux系统种查看某一个进程所占用的内存命令(0个评论)
    • Linux中grep命令中的10种高级用法浅析(0个评论)
    • 近期文章
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • Laravel 11.15版本发布 - Eloquent Builder中添加的泛型(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-11
    • 2017-07
    • 2017-10
    • 2017-11
    • 2018-01
    • 2018-02
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2021-02
    • 2021-03
    • 2021-04
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2021-12
    • 2022-01
    • 2022-03
    • 2022-04
    • 2022-08
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-06
    • 2023-07
    • 2023-10
    • 2023-12
    • 2024-01
    • 2024-04
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客