状态机实现的三种方法-C语言
1. 参考:https://www.cnblogs.com/aaronLinux/p/5705457.html
2. 转载:http://kb.cnblogs.com/page/528972/
3.参考:FSM-TCP读取文件:https://www.cnblogs.com/orange-CC/p/12933433.html
有限状态机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,有三种类型的事件:上层下达的命令事件;下层到达的标志和数据的收包事件;超时定时器超时事件。
图3可知,此TCP协议栈采用横着写方式实现,有3种事件处理函数,上层命令处理函数(如tcp_close);超时事件处理函数(tmr_slow);下层收包事件处理函数(tcp_process)。值得一提的是,在收包事件函数里,在各个状态里判断RST/SYN/FIN/ACK/DATA等标志(这些标志类似于事件),看起来象竖着写方式,其实,如果把包头和数据看成一个整体,那么,RST/SYN/FIN/ACK/DATA等标志就不必被看成独立的事件,而是属于同一个收包事件里的细节,这样,就不会认为在状态里查找事件,而是总体上看,是在收包事件里查找状态(横着写)。
在PPP里更是到处都能见到横着写的现象,有时间的话再细说。我个人感觉在实现PPP框架协议前必须了解横竖两种写法,而且只有使用横着写的方式才能比较完美地实现PPP。
状态机实现的三种方法-C语言相关推荐
- C语言函数怎么像python那样返回多个值?(三种方法:1、设置全局变量 2、传递指针 3、使用结构体返回不同类型的数据)
引用文章:c语言函数可不可以返回多个值 文章目录 方法一:设置全局变量 例如:利用一个函数求出正方形的周长和面积. 方法二:使用数组名或指针作为函数的形参 实例2:编写函数求一维整形数组的最大值与最小 ...
- c语言数组最大可定义多少位_C语言求数组的最大值三种方法
/* 黄哥Python培训 黄哥所写*/#include int maxValue(int* arr, int n);int maxRecursionValue(int* arr, int n);in ...
- C语言中三个数比较大小详解——三种方法
C语言中三个数比较大小详解--三种方法 方法一:if-else法 方法二:函数法 方法三:三目运算符法 C语言中比较三个数的大小有很多方法,以下是我总结的三种方法: 首先我定义 int a = 1 ...
- c语言程序π,C语言求圆周率π(三种方法)
题目1) 利用公式①计求π的近似值,要求累加到最后一项小于10^(-6)为止. 题目2) 根据公式②,用前100项之积计算π的值. 题目1)提供了一种解法,题目2)提供了两种解法,请看解析. 题目1) ...
- c语言求圆周率 . 4,C语言求圆周率π(三种方法)(4页)-原创力文档
C语言求圆周率π(三种方法) 题目1) 利用公式①计求π的近似值,要求累加到最后一项小于10^(-6)为止.题目2) 根据公式②,用前100项之积计算π的值.题目1)提供了一种解法,题目2)提供了两种 ...
- C语言求最大公约数三种方法详解
C语言求最大公约数三种方法详解 题目要求 常用写法(穷举法) 辗转相减法 辗转相除法 main函数 整体代码 题目要求 运行最大公约数的常用算法,并进行程序的调式与测试. 常用写法(穷举法) 从两个数 ...
- C语言实现不带头结点的单链表逆置的三种方法
C语言实现不带头结点的单链表逆置的三种方法 直接循环 头插法 递归法 END! 直接循环 图片解释 ListNode* ReverseList1(ListNode *head) {if(head == ...
- c语言编程非线性方程求解,c语言计算机编程三种方法求解非线性方程
c语言计算机编程三种方法求解非线性方程 本 科 专 业 学 年 论 文题 目:非线性方程求解比较姓 名: 何 娟 专 业: 计算机科学技术系 班 级: 08 级本科(2)班 指 导 老 师: 刘 晓 ...
- 易语言删除数组里的指定成员的三种方法
我们想从从数组里删除指定成员的时候,首先要遍历数组, 找到对应的成员名称,然后使用删除成员命令来删除.以下提供了三种思路. 方法一: 窗口程序集名 保 留 保 留 备 注 窗口程序集1 子程序名 返回 ...
最新文章
- Ubuntu 安装软件的三种方式
- sqlplus command
- 将C4C Service Request中的summary和其他附件同步到ERP的Billing Request去
- SamplePairing:针对图像处理领域的高效数据增强方式 | PaperDaily #34
- Cygwin,Nutch安装配置,检验是否正确(对网友守望者博客的修改---在此感谢守望者)4
- jqueryppt_jquery简单实现幻灯片的方法
- python argparse nargs_Python | 使用argparse解析命令行参数
- pycharm创建scrapy项目
- easyui-combobox 模糊匹配 支持汉字和拼音_巧用数据验证制作模糊匹配的下拉列表...
- 2020最新软件测试学习资料,全套源码无加密网盘下载
- U盘用USBOOT做引导盘后,导致无法格式化U盘
- 战争机器5加速器信息:开发工作已经完成 更多内容将在科隆展上披露
- lzg_ad:XPE常见问题FAQ
- Android广告的Activity收集
- python爬取知乎网页图片
- BUCK电源芯片做升压电源的方法(1)
- JS 简繁体互转代码
- 【错误集】 MonkeyPatchWarning: Monkey-patching ssl after ssl has already been imported may lead to errors
- 20154312 曾林 EXP9 Web安全基础
- 打印机打开扫描提示使用该设备需要WIA驱动程序。请从安装CD或从制造商的网站安装此程序,然后重试--------
热门文章
- 高质量项目管理-甘特图模板+教程(附下载包)/ PMP项目管理可用
- Java数组练习题百元百鸡,不死神兔
- 安装包时遇到 requires a peer of @angular/core、unmet peer dependency...的问题
- DSNet: A Flexible Detect-to-Summarize Network for Video Summarizationa论文笔记
- 篇一、Flask打造 Python Web 开发的灵活框架,实现简易登录。要求有 Python、HTML 和 CSS 基础。
- [开心幽默]一对北京情侣是如何吵架的 !(暴笑)
- 中点画线法(计算机图形学)
- 数据库三范式设计习题
- QT 中控件缩放比列设置
- Eclipse Maven 依赖包找不到源代码及javadoc