什么是状态机?

article/2025/9/29 17:43:17

前言

状态机在实际工作开发中应用非常广泛,在刚进入公司的时候,根据公司产品做流程图的时候,发现自己经常会漏了这样或那样的状态,导致整体流程会有问题,后来知道了状态机这样的东西,发现用这幅图就可以很清晰的表达整个状态的流转。

一口君曾经做过很多网络协议模块,很多协议的开发都必须用到状态机;一个健壮的状态机可以让你的程序,不论发生何种突发事件都不会突然进入一个不可预知的程序分支。

本篇通过C语言实现一个简单的进程5状态模型的状态机,让大家熟悉一下状态机的魅力。

什么是状态机?

定义

状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。

先来解释什么是“状态”( State )。现实事物是有不同状态的,例如一个LED等,就有 亮 和 灭两种状态。我们通常所说的状态机是有限状态机,也就是被描述的事物的状态的数量是有限个,例如LED灯的状态就是两个 亮和 灭。

状态机,也就是 State Machine ,不是指一台实际机器,而是指一个数学模型。说白了,一般就是指一张状态转换图。

举例

以物理课学的灯泡图为例,就是一个最基本的小型状态机

可以画出以下的状态机图

这里就是两个状态:①灯泡亮,②灯泡灭 如果打开开关,那么状态就会切换为 灯泡亮 。灯泡亮 状态下如果关闭开关,状态就会切换为 灯泡灭。

状态机的全称是有限状态自动机,自动两个字也是包含重要含义的。给定一个状态机,同时给定它的当前状态以及输入,那么输出状态时可以明确的运算出来的。例如对于灯泡,给定初始状态灯泡灭 ,给定输入“打开开关”,那么下一个状态时可以运算出来的。

四大概念

下面来给出状态机的四大概念。

  1. State ,状态。一个状态机至少要包含两个状态。例如上面灯泡的例子,有 灯泡亮和 灯泡灭两个状态。

  2. Event ,事件。事件就是执行某个操作的触发条件或者口令。对于灯泡,“打开开关”就是一个事件。

  3. Action ,动作。事件发生以后要执行动作。例如事件是“打开开关”,动作是“开灯”。编程的时候,一个 Action 一般就对应一个函数。

  4. Transition ,变换。也就是从一个状态变化为另一个状态。例如“开灯过程”就是一个变换。

状态机的应用

状态机是一个对真实世界的抽象,而且是逻辑严谨的数学抽象,所以明显非常适合用在数字领域。可以应用到各个层面上,例如硬件设计,编译器设计,以及编程实现各种具体业务逻辑的时候。

进程5状态模型

进程管理是Linux五大子系统之一,非常重要,实际实现起来非常复杂,我们来看下进程是如何切换状态的。

下图是进程的5状态模型:

关于该图简单介绍如下:

  1. 可运行态:当进程正在被CPU执行,或已经准备就绪随时可由调度程序执行,则称该进程为处于运行状态(running)。进程可以在内核态运行,也可以在用户态运行。当系统资源已经可用时,进程就被唤醒而进入准备运行状态,该状态称为就绪态。

  2. 浅度睡眠态(可中断):进程正在睡眠(被阻塞),等待资源到来是唤醒,也可以通过其他进程信号或时钟中断唤醒,进入运行队列。

  3. 深度睡眠态(不可中断):其和浅度睡眠基本类似,但有一点就是不可由其他进程信号或时钟中断唤醒。只有被使用wake_up()函数明确唤醒时才能转换到可运行的就绪状态。

  4. 暂停状态:当进程收到信号SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU时就会进入暂停状态。可向其发送SIGCONT信号让进程转换到可运行状态。

  5. 僵死状态:当进程已停止运行,但其父进程还没有询问其状态时,未释放PCB,则称该进程处于僵死状态。

进程的状态就是按照这个状态图进行切换的。

该状态流程有点复杂,因为我们目标只是实现一个简单的状态机,所以我们简化一下该状态机如下:

要想实现状态机,首先将该状态机转换成下面的状态迁移表。简要说明如下:假设当前进程处于running状态下,那么只有schedule事件发生之后,该进程才会产生状态的迁移,迁移到owencpu状态下,如果在此状态下发生了其他的事件,比如wake、wait_event都不会导致状态的迁移。

如上图所示:

  1. 每一列表示一个状态,每一行对应一个事件。

  2. 该表是实现状态机的最核心的一个图,请读者详细对比该表和状态迁移图的的关系。

  3. 实际场景中,进程的切换会远比这个图复杂,好在众多大神都帮我们解决了这些复杂的问题,我们只需要站在巨人的肩膀上就可以了。

实现

根据状态迁移表,定义该状态机的状态如下:

typedef enum {sta_origin=0,sta_running,sta_owencpu,sta_sleep_int,sta_sleep_unint
}State;

发生的事件如下:

typedef enum{evt_fork=0,evt_sched,evt_wait,evt_wait_unint,evt_wake_up,evt_wake, 
}EventID;

不论是状态还是事件都可以根据实际情况增加调整。

定义一个结构体用来表示当前状态转换信息:

typedef struct {State curState;//当前状态EventID eventId;//事件IDState nextState;//下个状态CallBack action;//回调函数,事件发生后,调用对应的回调函数
}StateTransform ; 

事件回调函数:实际应用中不同的事件发生需要执行不同的action,就需要定义不同的函数, 为方便起见,本例所有的事件都统一使用同一个回调函数。功能:打印事件发生后进程的前后状态,如果状态发生了变化,就调用对应的回调函数。

void action_callback(void *arg)
{StateTransform *statTran = (StateTransform *)arg;if(statename[statTran->curState] == statename[statTran->nextState]){printf("invalid event,state not change\n");}else{printf("call back state from %s --> %s\n",statename[statTran->curState],statename[statTran->nextState]);}
}

为各个状态定义迁移表数组:

/*origin*/
StateTransform stateTran_0[]={{sta_origin,evt_fork,        sta_running,action_callback},{sta_origin,evt_sched,       sta_origin,NULL},{sta_origin,evt_wait,        sta_origin,NULL},{sta_origin,evt_wait_unint,  sta_origin,NULL},{sta_origin,evt_wake_up,     sta_origin,NULL},{sta_origin,evt_wake,        sta_origin,NULL},
}; /*running*/
StateTransform stateTran_1[]={{sta_running,evt_fork,        sta_running,NULL},{sta_running,evt_sched,       sta_owencpu,action_callback},{sta_running,evt_wait,        sta_running,NULL},{sta_running,evt_wait_unint,  sta_running,NULL},{sta_running,evt_wake_up,     sta_running,NULL},{sta_running,evt_wake,        sta_running,NULL},
}; 
/*owencpu*/
StateTransform stateTran_2[]={{sta_owencpu,evt_fork,        sta_owencpu,NULL},{sta_owencpu,evt_sched,       sta_owencpu,NULL},{sta_owencpu,evt_wait,        sta_sleep_int,action_callback},{sta_owencpu,evt_wait_unint,  sta_sleep_unint,action_callback},{sta_owencpu,evt_wake_up,     sta_owencpu,NULL},{sta_owencpu,evt_wake,        sta_owencpu,NULL},
}; /*sleep_int*/
StateTransform stateTran_3[]={{sta_sleep_int,evt_fork,        sta_sleep_int,NULL},{sta_sleep_int,evt_sched,       sta_sleep_int,NULL},{sta_sleep_int,evt_wait,        sta_sleep_int,NULL},{sta_sleep_int,evt_wait_unint,  sta_sleep_int,NULL},{sta_sleep_int,evt_wake_up,     sta_sleep_int,NULL},{sta_sleep_int,evt_wake,        sta_running,action_callback},
}; 
/*sleep_unint*/
StateTransform stateTran_4[]={{sta_sleep_unint,evt_fork,        sta_sleep_unint,NULL},{sta_sleep_unint,evt_sched,       sta_sleep_unint,NULL},{sta_sleep_unint,evt_wait,        sta_sleep_unint,NULL},{sta_sleep_unint,evt_wait_unint,  sta_sleep_unint,NULL},{sta_sleep_unint,evt_wake_up,     sta_running,action_callback},{sta_sleep_unint,evt_wake,        sta_sleep_unint,NULL},
}; 

实现event发生函数:

void event_happen(unsigned int event)
功能:根据发生的event以及当前的进程state,找到对应的StateTransform 结构体,并调用do_action()
void do_action(StateTransform *statTran)
功能:根据结构体变量StateTransform,实现状态迁移,并调用对应的回调函数。
#define STATETRANS(n)  (stateTran_##n)
/*change state & call callback()*/
void do_action(StateTransform *statTran)
{if(NULL == statTran){perror("statTran is NULL\n");return;}//状态迁移globalState = statTran->nextState;if(statTran->action != NULL){//调用回调函数statTran->action((void*)statTran);}else{printf("invalid event,state not change\n");}
}
void event_happen(unsigned int event)
{switch(globalState){case sta_origin:do_action(&STATETRANS(0)[event]);break;case sta_running:do_action(&STATETRANS(1)[event]);break;case sta_owencpu:do_action(&STATETRANS(2)[event]); break;case sta_sleep_int:do_action(&STATETRANS(3)[event]); break;case sta_sleep_unint:do_action(&STATETRANS(4)[event]); break;default:printf("state is invalid\n");break;}
}

测试程序:功能:

  1. 初始化状态机的初始状态为sta_origin;

  2. 创建子线程,每隔一秒钟显示当前进程状态;

  3. 事件发生顺序为:evt_fork-->evt_sched-->evt_sched-->evt_wait-->evt_wake。

读者可以跟自己的需要,修改事件发生顺序,观察状态的变化。

main.c

/*显示当前状态*/
void *show_stat(void *arg)
{int len;char buf[64]={0};while(1){sleep(1);printf("cur stat:%s\n",statename[globalState]);} 
}
void main(void)
{init_machine();//创建子线程,子线程主要用于显示当前状态pthread_create(&pid, NULL,show_stat, NULL);sleep(5);event_happen(evt_fork);sleep(5);event_happen(evt_sched);sleep(5);event_happen(evt_sched);sleep(5);event_happen(evt_wait);sleep(5);event_happen(evt_wake);}

运行结果:由结果可知:

evt_fork-->evt_sched-->evt_sched-->evt_wait-->evt_wake

该事件发生序列对应的状态迁移顺序为:

origen-->running-->owencpu-->owencpu-->sleep_int-->running

http://chatgpt.dhexx.cn/article/5jukmaUM.shtml

相关文章

什么是状态机(Finite-state machine)?

有限状态机 有限状态机(FSM)1、 什么是“状态”2、什么是状态机?3、状态机图怎么画?参考 有限状态机(FSM) 1、 什么是“状态” 先来解释什么是“状态”( State )。现实事物是有不同状态的,例如一个自动门&#xff0c…

什么是状态机?用C语言实现进程5状态模型

前言 状态机在实际工作开发中应用非常广泛,在刚进入公司的时候,根据公司产品做流程图的时候,发现自己经常会漏了这样或那样的状态,导致整体流程会有问题,后来知道了状态机这样的东西,发现用这幅图就可以很…

状态机(state machine)

一、状态机分类 Mealy状态机:输出取决于输入和当前状态 状态寄存器:由一组触发器组成,用来记忆状态机当前所处的状态,状态的改变只发生在时钟的跳变沿。状态寄存器由一组触发器组成,用来记忆状态机当前所处的状态,状态的改变只发生在时钟的跳变沿。 状态是否改变、如何…

状态机(有限状态自动机 FSM)介绍以及常用状态机种类对比

目录 状态机概念 : 为什么需要状态机: 使用场景 状态机四要素: 常见类型状态机: Squirrel State Machine Spring Statemachine 状态机概念 : 概念 : 状态机是有限状态自动机(英语:finite-state machine,缩写:FSM&#xff…

STM32状态机编程----什么是状态机?

万事万物都有其状态 什么是状态 状态是人或事物表现出来的形态。是指现实(或虚拟)事物处于生成、生存、发展、消亡时期或各转化临界点时的形态或事物态势。 通过上面那句话,我们知道了状态就是一个对象在不同情况下对应的各种形态 做产品的…

什么是状态机?一篇文章就够了

1 概述 状态机[1]一般指有限状态机(英语:finite-state machine,缩写:FSM)又称有限状态自动机(英语:finite-state automaton,缩写:FSA),是表示有限…

C语言_有限状态机(FSM)

C语言_有限状态机(Finite State Machine) 基本介绍 许多小型或复杂的应用程序都使用有限状态机 (FSM),C 语言中的有限状态机是嵌入式系统的流行设计模式之一,有限状态机使开发变得容易和顺利。 有很多设备使用事件基态&#xf…

Unity字体展示下载

Unity字体种类展示 这是字体包里面的图片,是不是很多种字体. 下载链接: https://download.csdn.net/download/qq_42603590/12001130 这是下载字体包的地方,很便宜.没有积分的可以留言,我发给你 有时候可能回复的不是很快(抱拳了) 喜欢的话点个赞,关注一下再走吧,谢谢

Unity 之 官网下载地址,方便各个版本的 Unity 安装包下载

Unity 之 官网下载地址,方便各个版本的 Unity 安装包下载 目录 Unity 之 官网下载地址,方便各个版本的 Unity 安装包下载 一、简单介绍 二、各个版本下载入口网址 一、简单介绍 在 Unity 的下载地址现在不是很好找,这里保存一下 Unity 各…

Unity入门之路0-Unity下载安装以及版本选择

文章目录 下载链接Unity Hub和Unity的关系UnityHub下载(Win)两者比较 Unity版本选择许可证问题 下载链接 一定不要百度或者去垃圾网站下载盗版网站 ,Unity是正版免费的,官方很关注使用者的感受,所以下载官网的就没问题。 https://unity.cn/re…

UnityHub下载缓存位置

一、下载Unity各版本的编辑器 C:\Users\XXX\AppData\Local\Temp\unityhub-xxx-xxx-xxx-xxx 我电脑是 C:\Users\Administrator\AppData\Local\Temp\unityhub-xxxx-xxxx 如果你不需要备份安装包,那么这个缓存的文件夹,就与你无关了,因为安装完…

使用UnityHub下载任意版本Unity

目录 方法一 使用链接方法二 官网下载(适用于2018.4.23及以上版本) unityHub上只能下载官方指定的版本,很多其他版本不能下载,下面介绍的是在unityHub下载任意版本的方法 方法一 使用链接 举例: 2019.2.11f1版本的unity----> unityhub://2019.2.11f1/5f859a4cfee5 格式 unit…

Unity给游戏对象贴图、从官网下载资源、导入导出

1、新建项目、在项目场景中创建几何对象并修改参数 在层级“”中创建一个立方体(3D对象),同理也创建一个球体 创建好的立方体会显示在场景视图中 (从场景视图或层级视图中)选中几何体,选择场景视图中竖排工…

unity下载网页所有图片

用unity的c#脚本批量下载网页上的所有图片 1、将网页的html保存到本地 在网页上鼠标右击另存为如下图所示 保存html文件 2、通过截取<img“”>获取图片存储的地址 经过两个步骤之后就可以开始着手敲代码了 代码 html下载的本地地址和要保存的图片地址 //保存在本地ht…

Unity下载方法(超详细)

一、进入官网&#xff0c;点击[下载Unity]&#xff0c;点击右上角的小人头像&#xff0c;点击[创建Unity ID](创建ID的方法你点进去按照它要求你的一步一步做就行啦)。 二、创建完Unity ID并登录(或已有Unity ID并登录)后&#xff0c;下拉网页&#xff0c;点击[下载Unity Hub]&…

Unity 改变下载资源商店中资源默认路径的方法

Unity 改变下载资源商店中资源默认路径的方法 Unity资源商店中免费资源可以被我们很好的使用&#xff0c;尤其对于暂时还不会自己设计资源的创作者。但是&#xff0c;unity默认是将资源商店的下载路径设置在了C:\Users\操作系统当前用户\AppData\Roaming\Unity\Asset Store-5.x…

Unity 各版本下载方法

开发Unity的&#xff0c;获取不同版本Unity版本和了解Unity最新动态很重要&#xff0c;现在更新迭代很频繁&#xff0c;在开发时&#xff0c;不论遇到项目升级&#xff0c;还是插件要求&#xff0c;还是老项目运行&#xff0c;总是在多个版本间切换。 是不是经常遇到&#xff0…

Unity3d_NGUI和UGUI的学习

由于之前刚入门的时候&#xff0c;应Unity3d整体发展的要求我们自学了UGUI(相对来说UGUI比NGUI做得更好一些&#xff0c;后面会有2者对比)&#xff0c;但是后来公司要求使用NGUI&#xff0c;所以我这边把之前学习UGUI&#xff08;不全&#xff0c;当时资源有限&#xff09;和NG…

NGUI与UGUI的区别及其优缺点

UIGUI与BGUI 的区别 首先说一下NGUI NGUI是严格遵循KISS原则并用C#编写的Unity&#xff08;适用于专业版和免费版&#xff09;插件&#xff0c;提供强大的UI系统和事件通知框架。其代码简洁&#xff0c;多数类少于200行代码。这意味着程序员可以很容易地扩展NGUI的功能或调节已…

NGUI的使用教程与实例

原文地址&#xff1a;http://www.tasharen.com/?page_id185 NGUI下载地址&#xff1a;点我传送 NGUI教程&#xff1a;步骤1-Scene 1.创建一个新的场景&#xff08;New Scene&#xff09;。 2.选择并删除场景里的MainCamera。 3.在NGUI菜单下选择Create a New UI&#xff0c…