|
基本信息
状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动作,完成特定操作的控制中心。状态机分为摩尔(Moore)型状态机和米莉(Mealy)型状态机。 [1]
状态机就是状态转移图。举个最简单的例子,人有三个状态:健康,感冒,康复中。触发的条件有淋雨(t1),吃药(t2),打针(t3),休息(t4)。所以状态机就是健康-(t4)->健康;健康-(t1)->感冒;感冒-(t3)->健康;感冒-(t2)->康复中;康复中-(t4)->健康,等等。就是这样状态在不同的条件下跳转到自己或不同状态的图。
状态机综述
关于状态机的一个极度确切的描述是:它是一个有向图形,由一组节点和一组相应的转移函数组成。状态机通过响应一系列事件而“运行”。每个事件都在属于“当前” 节点的转移函数的控制范围内,其中函数的范围是节点的一个子集。函数返回“下一个”(也许是同一个)节点。这些节点中至少有一个必须是终态。当到达终态, 状态机停止。
包含一组状态集(states)、一个起始状态(start state)、一组输入符号集(alphabet)、一个映射输入符号和当前状态到下一状态的转换函数(transition function)的计算模型。当输入符号串,模型随即进入起始状态。它要改变到新的状态,依赖于转换函数。在有限状态机中,会有有许多变量,例如,状态 机有很多与动作(actions)转换(Mealy机)或状态(摩尔机)关联的动作,多重起始状态,基于没有输入符号的转换,或者指定符号和状态(非定有 限状态机)的多个转换,指派给接收状态(识别者)的一个或多个状态,等等。
传统应用程序的控制流程基本是顺序的:遵循事先设定的逻辑,从头到尾地执行。很少有事件能改变标准执行流程;而且这些事件主要涉及异常情况。“命令行实用程序”是这种传统应用程序的典型例子。
另一类应用程序由外部发生的事件来驱动——换言之,事件在应用程序之外生成,无法由应用程序或程序员来控制。具体需要执行的代码取决于接收到的事件,或者它相对于其他事件的抵达时间。所以,控制流程既不能是顺序的,也不能是事先设定好的,因为它要依赖于外部事件。事件驱动的GUI应用程序是这种应用程序的典 型例子,它们由命令和选择(也就是用户造成的事件)来驱动。
Web应用程序由提交的表单和用户请求的网页来驱动,它们也可划归到上述类别。但是,GUI应用程序对于接收到的事件仍有一定程度的控制,因为这些事件要依赖于向用户显示的窗口和控件,而窗口和控件是由程序员控制的。Web应用 程序则不然,因为一旦用户采取不在预料之中的操作(比如使用浏览器的历史记录、手工输入链接以及模拟一次表单提交等等),就很容易打乱设计好的应用程序逻辑。
显然,必须采取不同的技术来处理这些情况。它能处理任何顺序的事件,并能提供有意义的响应——即使这些事件发生的顺序和预计的不同。有限状态机正是为了满足这方面的要求而设计的。
有限状态机是一种概念性机器,它能采取某种操作来响应一个外部事件。具体采取的操作不仅能取决于接收到的事件,还能取决于各个事件的相对发生顺序。之所以能 做到这一点,是因为机器能跟踪一个内部状态,它会在收到事件后进行更新。为一个事件而响应的行动不仅取决于事件本身,还取决于机器的内部状态。另外,采取 的行动还会决定并更新机器的状态。这样一来,任何逻辑都可建模成一系列事件/状态组合。
[2] 状态机可归纳为4个要素,即现态、条件、动作、次态。这样的归纳,主要是出于对状态机的内在因果关系的考虑。“现态”和“条件”是因,“动作”和“次态”是果。详解如下:
①现态:是指当前所处的状态。
②条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
③动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
④次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。
两种写法
思想广泛应用于硬件控制电路设计,也是软件上常用的一种处理方法(软 件上称为FMM--有限消息机)。它把复杂的控制逻辑分解成有限个稳定状态,在每个状态上判断事件,变连续处理为离散数字处理,符合计算机的工作特点。同时,因为有限状态机具有有限个状态,所以可以在实际的工程上实现。但这并不意味着其只能进行有限次的处理,相反,有限状态机是闭环系统,有限无穷,可以用有限的状态,处理无穷的事务。
有限状态机的工作原理如图1所示,发生事件(event)后,根据当前状态(cur_state) ,决定执行的动作(action),并设置下一个状态号(nxt_state)。
表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;
}
} |
|