Linux有限状态机FSM的理解与实现
<p>有限状态机(finite state machine)简称fsm,表示有限个状态及在这些状态之间的转移和动作等行为的数学模型,在计算机领域有着广泛的应用。fsm是一种逻辑单元内部的一种高效编程方法,在服务器编程中,服务器可以根据不同状态或者消息类型进行相应的处理逻辑,使得程序逻辑清晰易懂。</p>
<p>
<span><strong>那有限状态机通常在什么地方被用到?</strong></span></p>
<p>
处理程序语言或者自然语言的 tokenizer,自底向上解析语法的parser,<br>
各种通信协议发送方和接受方传递数据对消息处理,游戏ai等都有应用场景。</p>
<p>
状态机有以下几种实现方法,我将一一阐述它们的优缺点。</p>
<p>
<strong>一、使用if/else if语句实现的fsm</strong><br>
使用if/else if语句是实现的fsm最简单最易懂的方法,我们只需要通过大量的if /else if语句来判断状态值来执行相应的逻辑处理。</p>
<p>
看看下面的例子,我们使用了大量的if/else if语句实现了一个简单的状态机,做到了根据状态的不同执行相应的操作,并且实现了状态的跳转。</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_584360">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
<div class="line number8 index7 alt1">
8</div>
<div class="line number9 index8 alt2">
9</div>
<div class="line number10 index9 alt1">
10</div>
<div class="line number11 index10 alt2">
11</div>
<div class="line number12 index11 alt1">
12</div>
<div class="line number13 index12 alt2">
13</div>
<div class="line number14 index13 alt1">
14</div>
<div class="line number15 index14 alt2">
15</div>
<div class="line number16 index15 alt1">
16</div>
<div class="line number17 index16 alt2">
17</div>
<div class="line number18 index17 alt1">
18</div>
<div class="line number19 index18 alt2">
19</div>
<div class="line number20 index19 alt1">
20</div>
<div class="line number21 index20 alt2">
21</div>
<div class="line number22 index21 alt1">
22</div>
<div class="line number23 index22 alt2">
23</div>
<div class="line number24 index23 alt1">
24</div>
<div class="line number25 index24 alt2">
25</div>
<div class="line number26 index25 alt1">
26</div>
<div class="line number27 index26 alt2">
27</div>
<div class="line number28 index27 alt1">
28</div>
<div class="line number29 index28 alt2">
29</div>
<div class="line number30 index29 alt1">
30</div>
<div class="line number31 index30 alt2">
31</div>
<div class="line number32 index31 alt1">
32</div>
<div class="line number33 index32 alt2">
33</div>
<div class="line number34 index33 alt1">
34</div>
<div class="line number35 index34 alt2">
35</div>
<div class="line number36 index35 alt1">
36</div>
<div class="line number37 index36 alt2">
37</div>
<div class="line number38 index37 alt1">
38</div>
<div class="line number39 index38 alt2">
39</div>
<div class="line number40 index39 alt1">
40</div>
<div class="line number41 index40 alt2">
41</div>
<div class="line number42 index41 alt1">
42</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">//</code><code class="bash plain">比如我们定义了小明一天的状态如下</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">enum</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash plain">{</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash spaces"> </code><code class="bash plain">get_up,</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash spaces"> </code><code class="bash plain">go_to_school,</code>
</div>
<div class="line number6 index5 alt1">
<code class="bash spaces"> </code><code class="bash plain">have_lunch,</code>
</div>
<div class="line number7 index6 alt2">
<code class="bash spaces"> </code><code class="bash plain">go_home,</code>
</div>
<div class="line number8 index7 alt1">
<code class="bash spaces"> </code><code class="bash plain">do_homework,</code>
</div>
<div class="line number9 index8 alt2">
<code class="bash spaces"> </code><code class="bash functions">sleep</code><code class="bash plain">,</code>
</div>
<div class="line number10 index9 alt1">
<code class="bash plain">};</code>
</div>
<div class="line number11 index10 alt2">
</div>
<div class="line number12 index11 alt1">
</div>
<div class="line number13 index12 alt2">
<code class="bash plain">int main()</code>
</div>
<div class="line number14 index13 alt1">
<code class="bash plain">{</code>
</div>
<div class="line number15 index14 alt2">
<code class="bash spaces"> </code><code class="bash plain">int state = get_up;</code>
</div>
<div class="line number16 index15 alt1">
<code class="bash spaces"> </code><code class="bash plain">//</code><code class="bash plain">小明的一天</code>
</div>
<div class="line number17 index16 alt2">
<code class="bash spaces"> </code><code class="bash keyword">while</code> <code class="bash plain">(1)</code>
</div>
<div class="line number18 index17 alt1">
<code class="bash spaces"> </code><code class="bash plain">{</code>
</div>
<div class="line number19 index18 alt2">
<code class="bash spaces"> </code><code class="bash keyword">if</code> <code class="bash plain">(state == get_up)</code>
</div>
<div class="line number20 index19 alt1">
<code class="bash spaces"> </code><code class="bash plain">{</code>
</div>
<div class="line number21 index20 alt2">
<code class="bash spaces"> </code><code class="bash plain">getup(); </code><code class="bash plain">//</code><code class="bash plain">具体调用的函数</code>
</div>
<div class="line number22 index21 alt1">
<code class="bash spaces"> </code><code class="bash plain">state = go_to_school; </code><code class="bash plain">//</code><code class="bash plain">状态的转移</code>
</div>
<div class="line number23 index22 alt2">
<code class="bash spaces"> </code><code class="bash plain">}</code>
</div>
<div class="line number24 index23 alt1">
<code class="bash spaces"> </code><code class="bash keyword">else</code> <code class="bash keyword">if</code> <code class="bash plain">(state == go_to_school)</code>
</div>
<div class="line number25 index24 alt2">
<code class="bash spaces"> </code><code class="bash plain">{</code>
</div>
<div class="line number26 index25 alt1">
<code class="bash spaces"> </code><code class="bash plain">go2school();</code>
</div>
<div class="line number27 index26 alt2">
<code class="bash spaces"> </code><code class="bash plain">state = have_lunch;</code>
</div>
<div class="line number28 index27 alt1">
<code class="bash spaces"> </code><code class="bash plain">}</code>
</div>
<div class="line number29 index28 alt2">
<code class="bash spaces"> </code><code class="bash keyword">else</code> <code class="bash keyword">if</code> <code class="bash plain">(state == have_lunch)</code>
</div>
<div class="line number30 index29 alt1">
<code class="bash spaces"> </code><code class="bash plain">{</code>
</div>
<div class="line number31 index30 alt2">
<code class="bash spaces"> </code><code class="bash plain">havelunch();</code>
</div>
<div class="line number32 index31 alt1">
<code class="bash spaces"> </code><code class="bash plain">}</code>
</div>
<div class="line number33 index32 alt2">
<code class="bash spaces"> </code><code class="bash plain">...</code>
</div>
<div class="line number34 index33 alt1">
<code class="bash spaces"> </code><code class="bash keyword">else</code> <code class="bash keyword">if</code> <code class="bash plain">(state == </code><code class="bash functions">sleep</code><code class="bash plain">)</code>
</div>
<div class="line number35 index34 alt2">
<code class="bash spaces"> </code><code class="bash plain">{</code>
</div>
<div class="line number36 index35 alt1">
<code class="bash spaces"> </code><code class="bash plain">go2bed();</code>
</div>
<div class="line number37 index36 alt2">
<code class="bash spaces"> </code><code class="bash plain">state = get_up;</code>
</div>
<div class="line number38 index37 alt1">
<code class="bash spaces"> </code><code class="bash plain">}</code>
</div>
<div class="line number39 index38 alt2">
<code class="bash spaces"> </code><code class="bash plain">}</code>
</div>
<div class="line number40 index39 alt1">
</div>
<div class="line number41 index40 alt2">
<code class="bash spaces"> </code><code class="bash keyword">return</code> <code class="bash plain">0;</code>
</div>
<div class="line number42 index41 alt1">
<code class="bash plain">}</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
</div>
<p>
看完上面的例子,大家有什么感受?是不是感觉程序虽然简单易懂,但是使用了大量的if判断语句,使得代码很低端,同时代码膨胀的比较厉害。这个状态机的状态仅有几个,代码膨胀并不明显,但是如果我们需要处理的状态有数十个的话,该状态机的代码就不好读了。</p>
<p>
<strong>二、使用switch实现fsm</strong></p>
<p>
使用switch语句实现的fsm的结构变得更为清晰了,其缺点也是明显的:这种设计方法虽然简单,通过一大堆判断来处理,适合小规模的状态切换流程,但如果规模扩大难以扩展和维护。</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_493498">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
<div class="line number8 index7 alt1">
8</div>
<div class="line number9 index8 alt2">
9</div>
<div class="line number10 index9 alt1">
10</div>
<div class="line number11 index10 alt2">
11</div>
<div class="line number12 index11 alt1">
12</div>
<div class="line number13 index12 alt2">
13</div>
<div class="line number14 index13 alt1">
14</div>
<div class="line number15 index14 alt2">
15</div>
<div class="line number16 index15 alt1">
16</div>
<div class="line number17 index16 alt2">
17</div>
<div class="line number18 index17 alt1">
18</div>
<div class="line number19 index18 alt2">
19</div>
<div class="line number20 index19 alt1">
20</div>
<div class="line number21 index20 alt2">
21</div>
<div class="line number22 index21 alt1">
22</div>
<div class="line number23 index22 alt2">
23</div>
<div class="line number24 index23 alt1">
24</div>
<div class="line number25 index24 alt2">
25</div>
<div class="line number26 index25 alt1">
26</div>
<div class="line number27 index26 alt2">
27</div>
<div class="line number28 index27 alt1">
28</div>
<div class="line number29 index28 alt2">
29</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">int main()</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">{</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash spaces"> </code><code class="bash plain">int state = get_up;</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash spaces"> </code><code class="bash plain">//</code><code class="bash plain">小明的一天</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash spaces"> </code><code class="bash keyword">while</code> <code class="bash plain">(1)</code>
</div>
<div class="line number6 index5 alt1">
<code class="bash spaces"> </code><code class="bash plain">{</code>
</div>
<div class="line number7 index6 alt2">
</div>
<div class="line number8 index7 alt1">
<code class="bash spaces"> </code><code class="bash plain">switch(state)</code>
</div>
<div class="line number9 index8 alt2">
<code class="bash spaces"> </code><code class="bash plain">{</code>
</div>
<div class="line number10 index9 alt1">
<code class="bash spaces"> </code><code class="bash keyword">case</code> <code class="bash plain">get_up:</code>
</div>
<div class="line number11 index10 alt2">
<code class="bash spaces"> </code><code class="bash plain">getup(); </code><code class="bash plain">//</code><code class="bash plain">具体调用的函数</code>
</div>
<div class="line number12 index11 alt1">
<code class="bash spaces"> </code><code class="bash plain">state = go_to_school; </code><code class="bash plain">//</code><code class="bash plain">状态的转移</code>
</div>
<div class="line number13 index12 alt2">
<code class="bash spaces"> </code><code class="bash keyword">break</code><code class="bash plain">;</code>
</div>
<div class="line number14 index13 alt1">
<code class="bash spaces"> </code><code class="bash keyword">case</code> <code class="bash plain">go_to_school:</code>
</div>
<div class="line number15 index14 alt2">
<code class="bash spaces"> </code><code class="bash plain">go2school();</code>
</div>
<div class="line number16 index15 alt1">
<code class="bash spaces"> </code><code class="bash plain">state = have_lunch;</code>
</div>
<div class="line number17 index16 alt2">
<code class="bash spaces"> </code><code class="bash keyword">break</code><code class="bash plain">;</code>
</div>
<div class="line number18 index17 alt1">
<code class="bash spaces"> </code><code class="bash keyword">case</code> <code class="bash plain">have_lunch:</code>
</div>
<div class="line number19 index18 alt2">
<code class="bash spaces"> </code><code class="bash plain">havelunch();</code>
</div>
<div class="line number20 index19 alt1">
<code class="bash spaces"> </code><code class="bash plain">state = go_home;</code>
</div>
<div class="line number21 index20 alt2">
<code class="bash spaces"> </code><code class="bash keyword">break</code><code class="bash plain">;</code>
</div>
<div class="line number22 index21 alt1">
<code class="bash spaces"> </code><code class="bash plain">...</code>
</div>
<div class="line number23 index22 alt2">
<code class="bash spaces"> </code><code class="bash plain">default:</code>
</div>
<div class="line number24 index23 alt1">
<code class="bash spaces"> </code><code class="bash keyword">break</code><code class="bash plain">;</code>
</div>
<div class="line number25 index24 alt2">
<code class="bash spaces"> </code><code class="bash plain">}</code>
</div>
<div class="line number26 index25 alt1">
<code class="bash spaces"> </code><code class="bash plain">}</code>
</div>
<div class="line number27 index26 alt2">
</div>
<div class="line number28 index27 alt1">
<code class="bash spaces"> </code><code class="bash keyword">return</code> <code class="bash plain">0;</code>
</div>
<div class="line number29 index28 alt2">
<code class="bash plain">}</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
</div>
<p>
<strong>三、使用函数指针实现fsm</strong></p>
<p>
使用函数指针实现fsm的思路:建立相应的状态表和动作查询表,根据状态表、事件、动作表定位相应的动作处理函数,执行完成后再进行状态的切换。</p>
<p>
当然使用函数指针实现的fsm的过程还是比较费时费力,但是这一切都是值得的,因为当你的程序规模大时候,基于这种表结构的状态机,维护程序起来也是得心应手。</p>
<p>
下面给出一个使用函数指针实现的fsm的框架:</p>
<p>
我们还是以“小明的一天”为例设计出该fsm。</p>
<p>
先给出该fsm的状态转移图:</p>
<p>
<img title="Linux有限状态机FSM的理解与实现" alt="Linux有限状态机FSM的理解与实现" src="https://zhuji.jb51.net/uploads/img/202305/539ea333a656d795f85c13c865a0c087.jpg"></p>
<p>
下面讲解关键部分代码实现</p>
<p>
首先我们定义出小明一天的活动状态</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_145110">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
<div class="line number8 index7 alt1">
8</div>
<div class="line number9 index8 alt2">
9</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">//</code><code class="bash plain">比如我们定义了小明一天的状态如下</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">enum</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash plain">{</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash spaces"> </code><code class="bash plain">get_up,</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash spaces"> </code><code class="bash plain">go_to_school,</code>
</div>
<div class="line number6 index5 alt1">
<code class="bash spaces"> </code><code class="bash plain">have_lunch,</code>
</div>
<div class="line number7 index6 alt2">
<code class="bash spaces"> </code><code class="bash plain">do_homework,</code>
</div>
<div class="line number8 index7 alt1">
<code class="bash spaces"> </code><code class="bash functions">sleep</code><code class="bash plain">,</code>
</div>
<div class="line number9 index8 alt2">
<code class="bash plain">};</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
</div>
<p>
我们也定义出会发生的事件</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_322010">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">enum</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">{</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash spaces"> </code><code class="bash plain">event1 = 1,</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash spaces"> </code><code class="bash plain">event2,</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash spaces"> </code><code class="bash plain">event3,</code>
</div>
<div class="line number6 index5 alt1">
<code class="bash plain">};</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
</div>
<p>
定义状态表的数据结构</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_509932">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">typedef struct fsmtable_s</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">{</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash spaces"> </code><code class="bash plain">int event; </code><code class="bash plain">//</code><code class="bash plain">事件</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash spaces"> </code><code class="bash plain">int curstate; </code><code class="bash plain">//</code><code class="bash plain">当前状态</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash spaces"> </code><code class="bash plain">void (*eventactfun)(); </code><code class="bash plain">//</code><code class="bash plain">函数指针</code>
</div>
<div class="line number6 index5 alt1">
<code class="bash spaces"> </code><code class="bash plain">int nextstate; </code><code class="bash plain">//</code><code class="bash plain">下一个状态</code>
</div>
<div class="line number7 index6 alt2">
<code class="bash plain">}fsmtable_t;</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
</div>
<p>
接下来定义出最重要fsm的状态表,我们整个fsm就是根据这个定义好的表来运转的。</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_144664">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
<div class="line number8 index7 alt1">
8</div>
<div class="line number9 index8 alt2">
9</div>
<div class="line number10 index9 alt1">
10</div>
<div class="line number11 index10 alt2">
11</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">fsmtable_t xiaomingtable[] =</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">{</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash spaces"> </code><code class="bash plain">//</code><code class="bash plain">{到来的事件,当前的状态,将要要执行的函数,下一个状态}</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash spaces"> </code><code class="bash plain">{ event1, </code><code class="bash functions">sleep</code><code class="bash plain">, getup, get_up },</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash spaces"> </code><code class="bash plain">{ event2, get_up, go2school, go_to_school },</code>
</div>
<div class="line number6 index5 alt1">
<code class="bash spaces"> </code><code class="bash plain">{ event3, go_to_school, havelunch, have_lunch },</code>
</div>
<div class="line number7 index6 alt2">
<code class="bash spaces"> </code><code class="bash plain">{ event1, have_lunch, dohomework, do_homework },</code>
</div>
<div class="line number8 index7 alt1">
<code class="bash spaces"> </code><code class="bash plain">{ event2, do_homework, go2bed, </code><code class="bash functions">sleep</code> <code class="bash plain">},</code>
</div>
<div class="line number9 index8 alt2">
</div>
<div class="line number10 index9 alt1">
<code class="bash spaces"> </code><code class="bash plain">//add</code> <code class="bash plain">your codes here</code>
</div>
<div class="line number11 index10 alt2">
<code class="bash plain">};</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
</div>
<p>
状态机的注册、状态转移、事件处理的动作实现</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_862945">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
<div class="line number8 index7 alt1">
8</div>
<div class="line number9 index8 alt2">
9</div>
<div class="line number10 index9 alt1">
10</div>
<div class="line number11 index10 alt2">
11</div>
<div class="line number12 index11 alt1">
12</div>
<div class="line number13 index12 alt2">
13</div>
<div class="line number14 index13 alt1">
14</div>
<div class="line number15 index14 alt2">
15</div>
<div class="line number16 index15 alt1">
16</div>
<div class="line number17 index16 alt2">
17</div>
<div class="line number18 index17 alt1">
18</div>
<div class="line number19 index18 alt2">
19</div>
<div class="line number20 index19 alt1">
20</div>
<div class="line number21 index20 alt2">
21</div>
<div class="line number22 index21 alt1">
22</div>
<div class="line number23 index22 alt2">
23</div>
<div class="line number24 index23 alt1">
24</div>
<div class="line number25 index24 alt2">
25</div>
<div class="line number26 index25 alt1">
26</div>
<div class="line number27 index26 alt2">
27</div>
<div class="line number28 index27 alt1">
28</div>
<div class="line number29 index28 alt2">
29</div>
<div class="line number30 index29 alt1">
30</div>
<div class="line number31 index30 alt2">
31</div>
<div class="line number32 index31 alt1">
32</div>
<div class="line number33 index32 alt2">
33</div>
<div class="line number34 index33 alt1">
34</div>
<div class="line number35 index34 alt2">
35</div>
<div class="line number36 index35 alt1">
36</div>
<div class="line number37 index36 alt2">
37</div>
<div class="line number38 index37 alt1">
38</div>
<div class="line number39 index38 alt2">
39</div>
<div class="line number40 index39 alt1">
40</div>
<div class="line number41 index40 alt2">
41</div>
<div class="line number42 index41 alt1">
42</div>
<div class="line number43 index42 alt2">
43</div>
<div class="line number44 index43 alt1">
44</div>
<div class="line number45 index44 alt2">
45</div>
<div class="line number46 index45 alt1">
46</div>
<div class="line number47 index46 alt2">
47</div>
<div class="line number48 index47 alt1">
48</div>
<div class="line number49 index48 alt2">
49</div>
<div class="line number50 index49 alt1">
50</div>
<div class="line number51 index50 alt2">
51</div>
<div class="line number52 index51 alt1">
52</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">/*状态机注册*/</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">void fsm_regist(fsm_t* pfsm, fsmtable_t* ptable)</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash plain">{</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash spaces"> </code><code class="bash plain">pfsm->fsmtable = ptable;</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash plain">}</code>
</div>
<div class="line number6 index5 alt1">
</div>
<div class="line number7 index6 alt2">
<code class="bash plain">/*状态迁移*/</code>
</div>
<div class="line number8 index7 alt1">
<code class="bash plain">void fsm_statetransfer(fsm_t* pfsm, int state)</code>
</div>
<div class="line number9 index8 alt2">
<code class="bash plain">{</code>
</div>
<div class="line number10 index9 alt1">
<code class="bash spaces"> </code><code class="bash plain">pfsm->curstate = state;</code>
</div>
<div class="line number11 index10 alt2">
<code class="bash plain">}</code>
</div>
<div class="line number12 index11 alt1">
</div>
<div class="line number13 index12 alt2">
<code class="bash plain">/*事件处理*/</code>
</div>
<div class="line number14 index13 alt1">
<code class="bash plain">void fsm_eventhandle(fsm_t* pfsm, int event)</code>
</div>
<div class="line number15 index14 alt2">
<code class="bash plain">{</code>
</div>
<div class="line number16 index15 alt1">
<code class="bash spaces"> </code><code class="bash plain">fsmtable_t* pacttable = pfsm->fsmtable;</code>
</div>
<div class="line number17 index16 alt2">
<code class="bash spaces"> </code><code class="bash plain">void (*eventactfun)() = null; </code><code class="bash plain">//</code><code class="bash plain">函数指针初始化为空</code>
</div>
<div class="line number18 index17 alt1">
<code class="bash spaces"> </code><code class="bash plain">int nextstate;</code>
</div>
<div class="line number19 index18 alt2">
<code class="bash spaces"> </code><code class="bash plain">int curstate = pfsm->curstate;</code>
</div>
<div class="line number20 index19 alt1">
<code class="bash spaces"> </code><code class="bash plain">int flag = 0; </code><code class="bash plain">//</code><code class="bash plain">标识是否满足条件</code>
</div>
<div class="line number21 index20 alt2">
<code class="bash spaces"> </code><code class="bash plain">int i;</code>
</div>
<div class="line number22 index21 alt1">
</div>
<div class="line number23 index22 alt2">
<code class="bash spaces"> </code><code class="bash plain">/*获取当前动作函数*/</code>
</div>
<div class="line number24 index23 alt1">
<code class="bash spaces"> </code><code class="bash keyword">for</code> <code class="bash plain">(i = 0; i<g_max_num; i++)</code>
</div>
<div class="line number25 index24 alt2">
<code class="bash spaces"> </code><code class="bash plain">{</code>
</div>
<div class="line number26 index25 alt1">
<code class="bash spaces"> </code><code class="bash plain">//</code><code class="bash plain">当且仅当当前状态下来个指定的事件,我才执行它</code>
</div>
<div class="line number27 index26 alt2">
<code class="bash spaces"> </code><code class="bash keyword">if</code> <code class="bash plain">(event == pacttable.event && curstate == pacttable.curstate)</code>
</div>
<div class="line number28 index27 alt1">
<code class="bash spaces"> </code><code class="bash plain">{</code>
</div>
<div class="line number29 index28 alt2">
<code class="bash spaces"> </code><code class="bash plain">flag = 1;</code>
</div>
<div class="line number30 index29 alt1">
<code class="bash spaces"> </code><code class="bash plain">eventactfun = pacttable.eventactfun;</code>
</div>
<div class="line number31 index30 alt2">
<code class="bash spaces"> </code><code class="bash plain">nextstate = pacttable.nextstate;</code>
</div>
<div class="line number32 index31 alt1">
<code class="bash spaces"> </code><code class="bash keyword">break</code><code class="bash plain">;</code>
</div>
<div class="line number33 index32 alt2">
<code class="bash spaces"> </code><code class="bash plain">}</code>
</div>
<div class="line number34 index33 alt1">
<code class="bash spaces"> </code><code class="bash plain">}</code>
</div>
<div class="line number35 index34 alt2">
</div>
<div class="line number36 index35 alt1">
</div>
<div class="line number37 index36 alt2">
<code class="bash spaces"> </code><code class="bash keyword">if</code> <code class="bash plain">(flag) </code><code class="bash plain">//</code><code class="bash plain">如果满足条件了</code>
</div>
<div class="line number38 index37 alt1">
<code class="bash spaces"> </code><code class="bash plain">{</code>
</div>
<div class="line number39 index38 alt2">
<code class="bash spaces"> </code><code class="bash plain">/*动作执行*/</code>
</div>
<div class="line number40 index39 alt1">
<code class="bash spaces"> </code><code class="bash keyword">if</code> <code class="bash plain">(eventactfun)</code>
</div>
<div class="line number41 index40 alt2">
<code class="bash spaces"> </code><code class="bash plain">{</code>
</div>
<div class="line number42 index41 alt1">
<code class="bash spaces"> </code><code class="bash plain">eventactfun();</code>
</div>
<div class="line number43 index42 alt2">
<code class="bash spaces"> </code><code class="bash plain">}</code>
</div>
<div class="line number44 index43 alt1">
</div>
<div class="line number45 index44 alt2">
<code class="bash spaces"> </code><code class="bash plain">//</code><code class="bash plain">跳转到下一个状态</code>
</div>
<div class="line number46 index45 alt1">
<code class="bash spaces"> </code><code class="bash plain">fsm_statetransfer(pfsm, nextstate);</code>
</div>
<div class="line number47 index46 alt2">
<code class="bash spaces"> </code><code class="bash plain">}</code>
</div>
<div class="line number48 index47 alt1">
<code class="bash spaces"> </code><code class="bash keyword">else</code>
</div>
<div class="line number49 index48 alt2">
<code class="bash spaces"> </code><code class="bash plain">{</code>
</div>
<div class="line number50 index49 alt1">
<code class="bash spaces"> </code><code class="bash plain">//</code> <code class="bash keyword">do</code> <code class="bash plain">nothing</code>
</div>
<div class="line number51 index50 alt2">
<code class="bash spaces"> </code><code class="bash plain">}</code>
</div>
<div class="line number52 index51 alt1">
<code class="bash plain">}</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
</div>
<p>
主函数我们这样写,然后观察状态机的运转情况</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_429093">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
<div class="line number8 index7 alt1">
8</div>
<div class="line number9 index8 alt2">
9</div>
<div class="line number10 index9 alt1">
10</div>
<div class="line number11 index10 alt2">
11</div>
<div class="line number12 index11 alt1">
12</div>
<div class="line number13 index12 alt2">
13</div>
<div class="line number14 index13 alt1">
14</div>
<div class="line number15 index14 alt2">
15</div>
<div class="line number16 index15 alt1">
16</div>
<div class="line number17 index16 alt2">
17</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">int main()</code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">{</code>
</div>
<div class="line number3 index2 alt2">
<code class="bash spaces"> </code><code class="bash plain">fsm_t fsm;</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash spaces"> </code><code class="bash plain">initfsm(&fsm);</code>
</div>
<div class="line number5 index4 alt2">
<code class="bash spaces"> </code><code class="bash plain">int event = event1; </code>
</div>
<div class="line number6 index5 alt1">
<code class="bash spaces"> </code><code class="bash plain">//</code><code class="bash plain">小明的一天,周而复始的一天又一天,进行着相同的活动</code>
</div>
<div class="line number7 index6 alt2">
<code class="bash spaces"> </code><code class="bash keyword">while</code> <code class="bash plain">(1)</code>
</div>
<div class="line number8 index7 alt1">
<code class="bash spaces"> </code><code class="bash plain">{</code>
</div>
<div class="line number9 index8 alt2">
<code class="bash spaces"> </code><code class="bash functions">printf</code><code class="bash plain">(</code><code class="bash string">"event %d is coming...\n"</code><code class="bash plain">, event);</code>
</div>
<div class="line number10 index9 alt1">
<code class="bash spaces"> </code><code class="bash plain">fsm_eventhandle(&fsm, event);</code>
</div>
<div class="line number11 index10 alt2">
<code class="bash spaces"> </code><code class="bash functions">printf</code><code class="bash plain">(</code><code class="bash string">"fsm current state %d\n"</code><code class="bash plain">, fsm.curstate);</code>
</div>
<div class="line number12 index11 alt1">
<code class="bash spaces"> </code><code class="bash functions">test</code><code class="bash plain">(&event); </code>
</div>
<div class="line number13 index12 alt2">
<code class="bash spaces"> </code><code class="bash functions">sleep</code><code class="bash plain">(1); </code><code class="bash plain">//</code><code class="bash plain">休眠1秒,方便观察</code>
</div>
<div class="line number14 index13 alt1">
<code class="bash spaces"> </code><code class="bash plain">}</code>
</div>
<div class="line number15 index14 alt2">
</div>
<div class="line number16 index15 alt1">
<code class="bash spaces"> </code><code class="bash keyword">return</code> <code class="bash plain">0;</code>
</div>
<div class="line number17 index16 alt2">
<code class="bash plain">}</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
</div>
<p>
看一看该状态机跑起来的状态转移情况:</p>
<p>
<img title="Linux有限状态机FSM的理解与实现" alt="Linux有限状态机FSM的理解与实现" id="theimg" src="https://zhuji.jb51.net/uploads/img/202305/ee0a2189d3279466ec2e0e636f084941.jpg"></p>
<p>
上面的图可以看出,当且仅当在指定的状态下来了指定的事件才会发生函数的执行以及状态的转移,否则不会发生状态的跳转。这种机制使得这个状态机不停地自动运转,有条不絮地完成任务。</p>
<p>
与前两种方法相比,使用函数指针实现fsm能很好用于大规模的切换流程,只要我们实现搭好了fsm框架,以后进行扩展就很简单了(只要在状态表里加一行来写入新的状态处理就可以了)。</p>
<p>
需要fsm完整代码的童鞋请访问我的github</p>
<p>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。</p>
頁:
[1]