当前位置: 首页 > 科技观察

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

时间:2023-03-20 15:39:53 科技观察

前言状态机在实际工作开发中应用广泛。刚进公司的时候,根据公司的产品做流程图的时候,发现经常漏掉一个状态,导致整个流程出现问题。后来学习了状态机之类的东西,发现这张图可以很清晰的表达出整个状态的流转。一扣君做了很多网络协议模块,很多协议的开发都要用到状态机;一个健壮的状态机可以使你的程序无论发生什么紧急情况都不会突然进入不可预知的程序分支。本文通过C语言实现一个简单的流程5状态模型状态机,让大家领略一下状态机的魅力。什么是状态机?定义状态机是有限状态自动机的简称,是从现实事物的运行规则中抽象出来的数学模型。我们先来解释一下什么是“状态”(State)。真实的事物有不同的状态。例如,LED有开和关两种状态。我们通常所说的状态机是有限状态机,即所描述事物的状态数是有限的。例如,LED灯的状态有两种亮和灭。状态机,即StateMachine,并不是指实际的机器,而是一种数学模型。说白了,一般指的是状态转移图。例如,以物理课上学的灯泡图为例。是最基本的小状态机,可以画出如下状态机图。这里有两种状态:①灯泡亮,②灯泡灭。如果开关打开,状态将切换到Thelightbulbison。如果在灯泡点亮时关闭开关,则状态将切换为灯泡关闭。状态机的全称是finitestateautomata,automatic这个词也包含着重要的含义。给定状态机,给定其当前状态和输入,可以显式计算输出状态。例如,对于一个灯泡,如果灯泡的初始状态是熄灭,给出输入“打开开关”,那么就可以计算出下一个状态。四个概念下面给出状态机的四个概念。状态,状态。状态机必须至少包含两个状态。比如上面那个灯泡的例子,有两种状态:灯泡亮着,灯泡灭着。事件,事件。事件是执行操作的触发条件或密码。对于灯泡来说,“打开开关”是一个事件。行动,行动。事件发生后要执行的操作。例如,事件是“打开开关”,动作是“开灯”。编程时,一个Action一般对应一个函数。过渡,转变。也就是说,从一种状态转变为另一种状态。比如“开灯的过程”就是一个变换。状态机的应用状态机是对现实世界的抽象,是一种逻辑严密的数学抽象,显然非常适合在数字领域使用。它可以应用于硬件设计、编译器设计、编程等各个层面,实现各种具体的业务逻辑。Process5状态模型进程管理是Linux的五个主要子系统之一。很重要,实际实现起来很复杂。让我们看看进程如何切换状态。下图是进程的5态模型:图的简单介绍如下:可运行状态:当进程正在被CPU执行,或者随时准备被调度器执行时,过程称为运行。进程可以在内核模式或用户模式下运行。当系统资源可用时,进程被唤醒,进入准备运行状态,称为就绪状态。轻度睡眠状态(可中断):进程处于睡眠(阻塞)状态,等待资源到来唤醒,也可以被其他进程信号或时钟中断唤醒,进入运行队列。Deepsleepstate(uninterruptible):与lightsleep基本类似,但有一点是不能被其他进程信号或时钟中断唤醒。只有在使用wake_up()函数显式唤醒时,才能转换到可运行就绪状态。挂起状态:当进程接收到信号SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU时,就会进入挂起状态。可以向它发送SIGCONT信号,使进程转换为可运行状态。Zombie状态:当进程已经停止运行,但其父进程还没有询问其状态,PCB也没有被释放时,称该进程处于死亡状态。进程的状态是根据这个状态图进行切换的。状态流程有点复杂,因为我们的目标是实现一个简单的状态机,所以我们将状态机简化如下:要实现状态机,首先将状态机转换成下面的状态转换表。简单描述如下:假设当前进程处于running状态,那么只有schedule事件发生后,进程才会迁移到owencpu状态。如果在此状态下发生其他事件,例如wake和wait_event不会引起状态转换。如上图所示:每一列代表一个状态,每一行对应一个事件。该表是实现状态机的核心图。请读者详细对比这张表和状态转移图的关系。在实际场景中,进程切换会远比这张图复杂。幸运的是,许多大师帮助我们解决了这些复杂的问题。我们只需站在巨人的肩膀上。根据状态转换表,状态机的状态定义如下:typedefenum{sta_origin=0,sta_running,sta_owencpu,sta_sleep_int,sta_sleep_unint}State;发生的事件如下:typedefenum{evt_fork=0,evt_sched,evt_wait,evt_wait_unint,evt_wake_up,evt_wake,}EventID;状态和事件都可以根据实际情况进行调整。定义一个表示当前状态转换信息的结构体:typedefstruct{StatecurState;//当前状态EventIDeventId;//事件IDStatenextState;//下一状态CallBackaction;//回调函数,事件发生后,调用相应的回调函数}StateTransform;事件回调函数:在实际应用中,不同的事件需要执行不同的动作,因此需要定义不同的函数。为方便起见,此示例中的所有事件都使用相同的回调函数。功能:打印事件发生前后进程的状态,如果状态发生变化则调用相应的回调函数。voidaction_callback(void*arg){StateTransform*statTran=(StateTransform*)arg;if(statename[statTran->curState]==statename[statTran->nextState]){printf("invalidevent,statnotchange\n");}else{printf("callbackstatefrom%s-->%s\n",statename[statTran->curState],statename[statTran->nextState]);}}为各状态定义义迁移表组:/*origin*/StateTransformstateTran_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*/StateTransformstateTran_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*/StateTransformstateTran_2[]={{sta_owencpu,possible_fork,sta_owencpu,NULL},{sta_owencpu,possible_sched,sta_owencpu,NULL},{sta_owencpu,possible_wait,sta_sleep_int,action_callback},{sta_owencpu,possible_wait_unit,sta_sleep_unit,wake,sta_upcallback,,{backpuke_action},sta_owencpu,NULL},{sta_owencpu,possible_wake,sta_owencpu,NULL},};/*sleep_int*/StateTransformStateTran_3[]={{sta_sleep_int,possible_fork,sta_sleep_int,NULL},{sta_sleep_int,possible_sched,sta_sleep_int,NULL},{瞬间,睡觉;possible_wait,sta_sleep_int,NULL},{sta_sleep_int,possible_wait_unit,sta_sleep_int,NULL},{sta_sleep_int,possible_wake_up,sta_sleep_int,NULL},{sta_sleep_int,possible_wake,sta_running,action_callback},};/*睡眠单位*[state=StateTran_fortep_unkint_s,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_unit,possible_wake,sta_sleep_unit,NULL},};实现事件发生函数:voidevent_happen(unsignedintevent)函数:根据事件和当前进程状态找到对应的StateTransform结构体,调用do_action()voiddo_action(StateTransform*statTran)函数:根据结构体变量StateTransform,实现状态转换,并调用相应的回调函数#defineSTATETRANS(n)(stateTran_##n)/*changestate&callcallback()*/voiddo_action(StateTransform*statTran){if(NULL==statTran){perror("statTranisNULL\n");return;}//状态转换globalState=statTran->nextState;if(statTran->action!=NULL){//调用回调函数statTran->action((void*)statTran);}else{printf("invalidevent,statenotchange\n");}}voidevent_happen(unsignedintevent){switch(globalState){casesta_origin:do_action(&STATETRANS(0)[event]);break;casesta_running:do_action(&STATETRANS(1)[event]);break;casesta_owencpu:do_action(&STATETRANS(2)[event]]);break;casesta_owencpu:do_action(&STATETRANS(2)[事件]);]);break;casesta_sleep_int:do_action(&STATETRANS(3)[event]);break;casesta_sleep_unint:do_action(&STATETRANS(4)[event]);break;default:printf("stateisinvalid\n");break;}}测试程序:功能:初始化状态机初始状态为sta_origin;创建子线程,每秒显示当前进程状态;事件的顺序是:evt_fork-->evt_sched-->evt_sched-->evt_wait-->evt_wake。读者可以根据自己的需要修改事件的顺序,观察状态的变化。main.c/*显示当前状态*/void*show_stat(void*arg){intlen;charbuf[64]={0};while(1){sleep(1);printf("curstat:%s\n",statename[globalState]);}}voidmain(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事件序列对应的状态转换顺序为:origin-->running-->owencpu-->owencpu-->sleep_int-->运行本文转载自微信公众号“一口Linux”,可通过以下二维码关注。转载本文请联系易口Linux公众号。