服務器設計系列:狀態機


分類: Nginx&高性能服務器架構 657人閱讀 評論(0) 收藏 舉報
    一、狀態機描述
    狀態機理論最初的發展在數字電路設計領域。在數字電路方面,根據輸出是否與輸入信號有關,狀態機可以劃分爲Mealy型和Moore型狀態機;根據輸出是否與輸入信號同步,狀態機可以劃分爲異步和同步狀態機。而在軟件設計領域,狀態機設計的理論儼然已經自成一體。Moore型狀態機的輸出只和當前狀態有關,和輸入無關,如果在軟件設計領域設計出這種類型的狀態機,則該狀態機接受的事件都是無內蘊信息的事件(輸入)。Mealy型狀態機的輸入是由當前狀態和輸入共同決定,對應到軟件設計領域,則該狀態機接收的事件含有內蘊信息,並且影響狀態機的輸出。顯然,這種劃分在軟件設計領域毫無意義。雖然軟件設計領域的狀態機也有同步和異步的劃分,但和數字電路方面的同步異步已經不同。

    除了《數字電路》,涉及到狀態機的課程就是《編譯原理》了。下面簡單回顧一下《編譯原理》裏有關有限狀態機的描述。在編譯原理課程裏面,對有限狀態機的描述僅限在編譯領域,特定狀態,針對輸入字符,發生狀態改變,沒有額外的行爲,另編譯原理裏有限狀態機的構成要素,還包含唯一的初始狀態和一個終態集。數學語言描述如下:

    一個有限狀態機M是一個五元組,M=(K,E,T,S,Z)。其中

    (1)K是一個有窮集,其中的每個元素稱爲狀態

    (2)E是一個有窮字母表,它的每個元素稱爲一個輸入字符

    (3)T是轉換函數,是K×E->K上的映射

    (4)S是K中的元素,是唯一的一個初態

    (5) Z是K的一個子集,是一個終態集,或者叫結束集。

    很明顯,狀態機在編譯原理裏的講解已經特化,輸入被定位爲字符集,狀態改變的時候沒有額外動作發生。

    與編譯原理中的狀態機不同,軟件設計領域中通用狀態機的輸入不是字符集,而是被稱作事件的結構(可以是結構體,也可以是類對象),並且特定的狀態下,針對發生的事件,不僅發生狀態改變,而且產生動作。借鑑編譯原理中狀態機的初始狀態和終態,通用狀態機的數學語言描述如下:

    一個通用有限狀態機M是一個七元組,M={K,E,T,M,F,S,Z}。其中

    (1)K是一個有窮集,其中的每個元素稱爲狀態

    (2)E是一個有窮集,它的每個元素稱爲一個事件

    (3)T是轉換函數,是K×E->K上的映射

    (4)M是一個有窮集,它的每個元素稱爲動作

    (5)F是動作映射函數,是K×E->M上的映射

    (6)S是K中的元素,是唯一的一個初態

    (7) Z是K的一個子集,是一個終態集,或者叫結束集。

    實用的狀態機可以做進一步的優化,首先,可以把 (3)(5)整合在一起,做一個K×E->{K,M}的映射,其次從實用性的角度出發,禁止狀態接收空事件(無輸入的情況下,狀態發生改變),作爲彌補,爲每個狀態增加進入動作和離開動作,第三,鑑於定時器在系統中,尤其是在狀態機中的重要性,可以爲每個狀態增加定時器以及超時後的狀態轉換。本文後面的講述以及實現暫不考慮把定時器特化,如果需要,可以在狀態的進入動作中初始化定時器。

    二、狀態機分類

    後文中如無特別說明,則狀態機指軟件設計領域的通用有限狀態機。依據狀態之間是否有包含關係,分以下兩種:

    (1)常規狀態機。狀態機中的所有狀態是不相交的、互斥的。

    (2)層次狀態機。狀態機中的狀態之間要麼是互斥的,要麼是真包含的,可以用樹型結構來描述這些狀態集,包含其它狀態的狀態稱爲枝節點,不包含其它狀態的狀態稱爲葉節點,爲方便用樹描述,總是設計一個狀態包含所有的狀態節點,稱爲根節點。狀態機的狀態只能停留在葉節點,而不能停留在枝節點,每個枝節點需要指定一個子節點爲它的默認子節點,以便狀態機進入枝節點的時候能夠停留到葉節點。

    三、狀態機實現
    對於少量狀態(3個及其以下),可用switch/case或if/else方式實現,不需要引入專門的狀態機模塊。這種方式不能編寫通用的狀態機模塊,不再多說。一般狀態機可採用面向過程方式實現。宏是實現面向過程方式的通用方式。雖然在狀態機層面還是可以用面向對象的方式封裝,這裏還是把它稱爲面向過程的方式。
    1、常規狀態機模塊實現

    這個狀態機涉及到的結構由上而下爲:
    頂層結構是狀態機:一個狀態機包含當前狀態ID,缺省操作,狀態表
    狀態表:狀態數組
    狀態結構:一個狀態包含狀態id,狀態名,進入操作,退出操作,缺省操作,狀態事件表(數組)
    狀態事件結構:一個事件包含操作函數,事件ID,下一狀態的ID
    狀態機的算法是由狀態機的結構決定的。實現如下:

  1. #define SINGLE_STATE_MAX_EVENT 10  
  2. typedef int FSM_EVENT_ID;  
  3. /* 事件參數結構 */  
  4. typedef struct event_param_st  
  5. {  
  6.     FSM_EVENT_ID id;  /* 所屬事件ID */  
  7.     union{  
  8.         int i;  
  9.     }data;    /* 數據 */  
  10. }FSM_EVENT;  
  11. typedef int FSM_STATE_ID;  
  12. typedef void (*FSM_FUNC)(FSM_EVENT *);  /* 事件函數指針 */  
  13. /* 狀態事件結構 */  
  14. typedef struct state_event_st  
  15. {  
  16.     FSM_FUNC func;       /* 事件函數 */  
  17.     FSM_EVENT_ID event;  /* 事件 */  
  18.     FSM_STATE_ID state;  /* 下一狀態ID */  
  19. }FSM_STATE_EVENT;  
  20. /* 狀態結構 */  
  21. typedef struct state_st  
  22. {  
  23.     FSM_STATE_ID id;        /* 狀態ID */  
  24.     char *name;             /* 狀態名 */  
  25.     FSM_FUNC enter_func;    /* 進入操作 */  
  26.     FSM_FUNC exit_func;     /* 退出操作 */  
  27.     FSM_FUNC default_func;  /* 缺省操作 */  
  28.     FSM_STATE_EVENT event_table[SINGLE_STATE_MAX_EVENT];   /* 事件表 */  
  29. }FSM_STATE;  
  30.   
  31. typedef FSM_STATE STATE_TABLE[];     /* 狀態表 */  
  32. typedef FSM_STATE * PTR_STATE_TABLE;  
  33. #define END_EVENT_ID -1  
  34. #define END_STATE_ID -1  
  35. #define BEGIN_FSM_STATE_TABLE(state_stable) static STATE_TABLE state_stable={  
  36. #define BEGIN_STATE(id,name,enter_func,exit_func,default_func) {id,name,enter_func,exit_func,default_func,{  
  37. #define STATE_EVENT_ITEM(func,event,state) {func,event,state},  
  38. #define END_STATE(id) {NULL,END_EVENT_ID,END_STATE_ID}}},  
  39. #define END_FSM_STATE_TABLE(state_stable) {END_STATE_ID,NULL,NULL,NULL,NULL,NULL}};  
  40.   
  41. /* 表示一個狀態機 */  
  42. typedef struct fsm_st  
  43. {  
  44.     FSM_STATE_ID state_id;         /* 當前狀態ID */  
  45.     FSM_FUNC default_func;         /* 缺省操作 */  
  46.     PTR_STATE_TABLE state_tables;  /* 狀態表 */  
  47.       
  48. }FSM;  
  49.   
  50. /* 對狀態機fsm中的當前狀態,觸發事件event */  
  51. void fsm_do_event(FSM &fsm, FSM_EVENT &event)  
  52. {  
  53.     FSM_STATE *state=&(fsm.state_tables[fsm.state_id]);  /* 得到當前狀態 */  
  54.     int i=0;  
  55.     /* 在當前狀態的事件表中搜索event所屬的事件 */  
  56.     while(state->event_table[i].event!=END_EVENT_ID)  
  57.     {  
  58.         if(state->event_table[i].event==event.id)  
  59.             break;  
  60.         i++;  
  61.     }     
  62.     if(state->event_table[i].event!=END_EVENT_ID)  /* 找到事件 */  
  63.     {  
  64.         /* 退出當前狀態 */  
  65.         if(state->id!=state->event_table[i].state)  
  66.         {  
  67.             if(state->exit_func )   
  68.                 state->exit_func(&event);  
  69.         }  
  70.         /* 執行事件操作 */  
  71.         if(state->event_table[i].func)  
  72.             state->event_table[i].func(&event);  
  73.         /* 進入下一狀態 */  
  74.         if(state->id!=state->event_table[i].state)  
  75.         {  
  76.             if(fsm.state_tables[state->event_table[i].state].enter_func)   
  77.                 fsm.state_tables[state->event_table[i].state].enter_func(&event);  
  78.             fsm.state_id=state->event_table[i].state;  /* 更新狀態機的當前狀態ID */  
  79.         }  
  80.     }  
  81.     else  /* 沒有找到事件 */  
  82.     {  
  83.         if(state->default_func)    /* 執行當前狀態的缺省操作 */  
  84.             state->default_func(&event);  
  85.         else  
  86.         {  
  87.             if(fsm.default_func)  /* 執行狀態機的缺省操作 */  
  88.                 fsm.default_func(&event);  
  89.         }  
  90.     }  
  91. }  
    以上說明實現原理,有特殊需要的話可以自己定製狀態機,比如上面的狀態事件表數組的上限取的是單個狀態中事件項的最大值,也可以定義爲所有事件的個數,這樣的話事件也不需要查詢,可以象狀態樣直接定位,只是狀態事件表會浪費一些存儲空間。上面的FSM_EVENT僅僅是個例子,實際開發根據需要定義不同的union。上面的算法也是假定狀態表的狀態定義是從0開始,順序遞增的。
    對外部調用而言,最後的狀態機結構和事件執行的方法可以封裝爲對象。下面舉例說明狀態機的定義及使用演示(事件和狀態都應該是enum類型,這裏直接使用數字,僅爲說明問題而已)。
  1. #include <stdio.h>  
  2.   
  3. //演示一個具體的狀態機  
  4. void enter_fsm(FSM_EVENT * event)  //狀態進入操作  
  5. {  
  6.     printf("enter me\n");  
  7. }  
  8. void exit_fsm(FSM_EVENT * event)  //狀態退出操作  
  9. {  
  10.     printf("exit me\n");  
  11. }  
  12. void default_fsm(FSM_EVENT * event)  //缺省操作  
  13. {  
  14.     printf("I am default_fsm\n");  
  15. }  
  16. void func_fsm(FSM_EVENT * event)    //事件操作  
  17. {  
  18.     printf("I am func_fsm\n");  
  19. }  
  20.   
  21. //定義一個具體的狀態表  
  22. BEGIN_FSM_STATE_TABLE(my_state_table)  
  23.     BEGIN_STATE(0,"first",enter_fsm,exit_fsm,default_fsm)  
  24.         STATE_EVENT_ITEM(func_fsm,1,1)  
  25.         STATE_EVENT_ITEM(func_fsm,2,2)  
  26.     END_STATE(0)  
  27.       
  28.     BEGIN_STATE(1,"second",enter_fsm,exit_fsm,default_fsm)  
  29.         STATE_EVENT_ITEM(func_fsm,1,2)  
  30.         STATE_EVENT_ITEM(func_fsm,2,0)  
  31.     END_STATE(1)  
  32.       
  33.     BEGIN_STATE(2,"third",enter_fsm,exit_fsm,default_fsm)  
  34.         STATE_EVENT_ITEM(func_fsm,1,0)  
  35.         STATE_EVENT_ITEM(func_fsm,2,1)  
  36.     END_STATE(2)  
  37. END_FSM_STATE_TABLE(my_state_table)  
  38.   
  39. int main(int argc, char* argv[])  
  40. {  
  41.     printf("I am main\n");  
  42.     //定義一個狀態機  
  43.     FSM fsm={0,default_fsm,my_state_table};  
  44.     //打印當前狀態ID和名稱  
  45.     printf("state[%d],name[%s]\n",fsm.state_id,fsm.state_tables[fsm.state_id].name);  
  46.     FSM_EVENT event;  //事件  
  47.     event.id=1;  
  48.     event.data.i=1;  
  49.     fsm_do_event(fsm,event);  //從當前狀態0轉移到狀態1  
  50.     printf("state[%d],name[%s]\n",fsm.state_id,fsm.state_tables[fsm.state_id].name);  
  51. }  
    程序輸出如下:

[plain] view plaincopy
  1. I am main  
  2. state[0],name[first]  
  3. exit me  
  4. I am func_fsm  
  5. enter me  
  6. state[1],name[second]  
    2、層次狀態機模塊實現
    與常規狀態機相比,它的FSM_STATE結構沒有default_func,多了FSM_STATE_ID parent; FSM_STATE_ID default_child;兩個結構。狀態機初始化的時候可以指定默認狀態,爲了防止指定的狀態非葉結點,增加fsm_init方法。該狀態機的事件處理算法簡單描述如下:

    (1)首先在當前狀態以及其祖先狀態的狀態事件表中搜索匹配事件,如果搜索到,保存操作以及目的狀態標識;

    (2)在old棧中保存當前狀態到根節點的路徑,在new棧中保存目的狀態到根節點的路徑;

    (3)將old棧中的頂層元素依次與new棧的頂層元素匹配,如果匹配則都出棧,不匹配,停止;

    (4)當前的old棧中節點即爲該事件導致的退出狀態,從棧低掃描到棧頂,依次執行exit_func;

    (5)執行以前保存的操作;

    (6)掃描new棧,從棧頂到棧低依次執行enter_func;

    (7)最後檢測目的狀態是否是葉節點狀態,否,則依次進入default_child節點,並執行enter_func。

    模塊實現代碼如下:

  1. #define SINGLE_STATE_MAX_EVENT 10  
  2. #define STATE_TREE_DEPTH 10  
  3. typedef  int FSM_EVENT_ID;  
  4. /* 事件參數結構 */  
  5. typedef struct event_param_st  
  6. {  
  7.     FSM_EVENT_ID id;  /* 所屬事件ID */  
  8.     union{  
  9.         int i;  
  10.     }data;    /* 數據 */  
  11. }FSM_EVENT;  
  12. typedef  int FSM_STATE_ID;  
  13. typedef void (*FSM_FUNC)(FSM_EVENT *);  
  14. /* 狀態事件結構 */  
  15. typedef struct state_event_st  
  16. {  
  17.     FSM_FUNC func;       /* 事件函數 */  
  18.     FSM_EVENT_ID event;  /* 事件 */  
  19.     FSM_STATE_ID state;  /* 下一狀態ID */  
  20. }FSM_STATE_EVENT;  
  21. /* 狀態結構 */  
  22. typedef struct state_st  
  23. {  
  24.     FSM_STATE_ID id;        /* 狀態ID */  
  25.     char *name;             /* 狀態名 */  
  26.     FSM_STATE_ID parent;    /* 父狀態ID */  
  27.     FSM_STATE_ID default_child;  /* 默認子狀態ID */  
  28.     FSM_FUNC enter_func;    /* 進入操作 */  
  29.     FSM_FUNC exit_func;     /* 退出操作 */  
  30.     FSM_STATE_EVENT event_table[SINGLE_STATE_MAX_EVENT];    /* 事件表 */  
  31. }FSM_STATE;  
  32. typedef FSM_STATE STATE_TABLE[];     /* 狀態表 */  
  33. typedef FSM_STATE * PTR_STATE_TABLE;  
  34.   
  35. #define END_EVENT_ID -1  
  36. #define END_STATE_ID -1  
  37. #define BEGIN_FSM_STATE_TABLE(state_stable) static STATE_TABLE state_stable={  
  38. #define BEGIN_STATE(id,name,parent,default_child,enter_func,exit_func) {id,name,parent,default_child,enter_func,exit_func,{  
  39. #define STATE_EVENT_ITEM(func,event,state) {func,event,state},  
  40. #define END_STATE(id) {NULL,END_EVENT_ID,END_STATE_ID}}},  
  41. #define END_FSM_STATE_TABLE(state_stable) {END_STATE_ID,NULL,END_STATE_ID,END_STATE_ID,NULL,NULL,NULL}};  
  42.   
  43. /* 表示一個狀態機 */  
  44. typedef struct fsm_st  
  45. {  
  46.     FSM_STATE_ID state_id;         /* 當前狀態ID */  
  47.     FSM_FUNC default_func;         /* 缺省操作 */  
  48.     PTR_STATE_TABLE state_tables;  /* 狀態表 */  
  49. }FSM;  
  50.   
  51. //初始化:依次進入當前狀態的默認子狀態  
  52. void fsm_init(FSM &fsm)  
  53. {  
  54.     FSM_STATE *state=&(fsm.state_tables[fsm.state_id]);  //得到當前狀態  
  55.     //依次進入所有的默認子狀態  
  56.     while(state->default_child!=END_STATE_ID)  
  57.     {  
  58.         state=&(fsm.state_tables[state->default_child]);  
  59.         if(state->enter_func)  
  60.             state->enter_func(NULL);  
  61.     }  
  62.     fsm.state_id=state->id;  
  63. }  
  64. void fsm_do_event(FSM &fsm, FSM_EVENT &event)  
  65. {  
  66.     FSM_STATE *state;  
  67.     FSM_STATE_ID state_id,old_state_id,new_state_id;  
  68.     FSM_STATE_ID oldStack[STATE_TREE_DEPTH],newStack[STATE_TREE_DEPTH];  
  69.     int old_cur=0,new_cur=0;  
  70.       
  71.     bool isMatch=false;  
  72.     FSM_FUNC match_func=NULL;  
  73.     int i=0;  
  74.     state_id=old_state_id=fsm.state_id;  
  75.     //在當前狀態及其祖先狀態的事件表中搜索匹配事件  
  76.     //保存其事件函數和目的狀態ID  
  77.     do  
  78.     {  
  79.         i=0;  
  80.         state=&(fsm.state_tables[state_id]);  
  81.         while(state->event_table[i].event!=END_EVENT_ID)  
  82.         {  
  83.             if(state->event_table[i].event==event.id)  
  84.             {  
  85.                 isMatch=true;  
  86.                 match_func=state->event_table[i].func;  
  87.                 new_state_id=state->event_table[i].state;  
  88.                 break;  
  89.             }  
  90.             i++;  
  91.         }  
  92.         if(isMatch==false)  
  93.             state_id=state->parent;  
  94.         else  
  95.             break;  
  96.     }while(state->parent!=END_STATE_ID);  
  97.     //沒找到則運行默認函數  
  98.     if(isMatch==false)  
  99.     {  
  100.         if(fsm.default_func)  
  101.             fsm.default_func(&event);  
  102.         return;  
  103.     }  
  104.     //狀態無需轉移,直接觸發事件函數  
  105.     if(new_state_id==old_state_id)  
  106.     {  
  107.         if(match_func)  
  108.             match_func(&event);  
  109.         return;  
  110.     }  
  111.     state_id=old_state_id;  
  112.     //在old棧中保存當前狀態到根節點的路徑  
  113.     do  
  114.     {  
  115.         oldStack[old_cur++]=state_id;  
  116.         state=&(fsm.state_tables[state_id]);  
  117.         state_id=state->parent;  
  118.     }while(state->parent!=END_STATE_ID);  
  119.     state_id=new_state_id;  
  120.     //在new棧中保存目的狀態到根節點的路徑  
  121.     do  
  122.     {  
  123.         newStack[new_cur++]=state_id;  
  124.         state=&(fsm.state_tables[state_id]);  
  125.         state_id=state->parent;  
  126.     }while(state->parent!=END_STATE_ID);  
  127.     //將old棧中的頂層元素依次與new棧的頂層元素匹配,若匹配則都出棧,不匹配則停止  
  128.     while(oldStack[old_cur-1]==newStack[new_cur-1])  
  129.     {  
  130.         old_cur--;  
  131.         new_cur--;  
  132.     }  
  133.     //退出當前狀態:當前的old棧中節點即爲該事件導致的退出狀態,從棧低掃描到棧頂,依次執行exit_func  
  134.     for(i=0;i<old_cur;i++)  
  135.     {  
  136.         if(fsm.state_tables[oldStack[i]].exit_func)  
  137.             fsm.state_tables[oldStack[i]].exit_func(&event);  
  138.     }  
  139.     //觸發事件函數  
  140.     if(match_func)  
  141.         match_func(&event);  
  142.     //進入目的狀態:掃描new棧,從棧頂到棧低依次執行enter_func  
  143.     for(i=new_cur;i>0;i--)  
  144.     {  
  145.         if(fsm.state_tables[newStack[i-1]].enter_func)  
  146.             fsm.state_tables[newStack[i-1]].enter_func(&event);  
  147.     }  
  148.     //最後檢測目的狀態是否是葉節點狀態,否,則依次進入default_child節點,並執行enter_func  
  149.     state=&(fsm.state_tables[new_state_id]);  
  150.     while(state->default_child!=END_STATE_ID)  
  151.     {  
  152.         state=&(fsm.state_tables[state->default_child]);  
  153.         if(state->enter_func)  
  154.             state->enter_func(&event);  
  155.     }  
  156.     fsm.state_id=state->id;  
  157. }  
    使用舉例,僅僅列舉一個狀態表和簡單的狀態機初始化,狀態和事件應該爲enum,當前使用數字,僅爲了舉例,操作的實現不在寫出。
  1. //一個狀態表  
  2. BEGIN_FSM_STATE_TABLE(my_state_table)  
  3.     BEGIN_STATE(0,"first",END_STATE_ID,2,enter_fsm,exit_fsm)  
  4.         STATE_EVENT_ITEM(func_fsm,1,1)  
  5.         STATE_EVENT_ITEM(func_fsm,2,2)  
  6.     END_STATE(0)  
  7.       
  8.     BEGIN_STATE(1,"second",0,END_STATE_ID,enter_fsm,exit_fsm)  
  9.         STATE_EVENT_ITEM(func_fsm,1,3)  
  10.         STATE_EVENT_ITEM(func_fsm,2,0)  
  11.     END_STATE(1)  
  12.       
  13.     BEGIN_STATE(2,"third",0,3,enter_fsm,exit_fsm)  
  14.         STATE_EVENT_ITEM(func_fsm,1,0)  
  15.         STATE_EVENT_ITEM(func_fsm,2,1)  
  16.     END_STATE(2)  
  17.     BEGIN_STATE(3,"third",2,END_STATE_ID,enter_fsm,exit_fsm)  
  18.         STATE_EVENT_ITEM(func_fsm,1,4)  
  19.         STATE_EVENT_ITEM(func_fsm,2,1)  
  20.     END_STATE(3)  
  21.     BEGIN_STATE(4,"third",2,END_STATE_ID,enter_fsm,exit_fsm)  
  22.         STATE_EVENT_ITEM(func_fsm,1,2)  
  23.         STATE_EVENT_ITEM(func_fsm,2,1)  
  24.     END_STATE(4)  
  25. END_FSM_STATE_TABLE(my_state_table)  
  26.   
  27. //狀態機的初始化  
  28. FSM fsm={0,default_fsm,my_state_table};  
  29. fsm_init(fsm);  
  30. FSM_EVENT event;  
  31. event.id=1;  
  32. event.data.i=1;  
  33. fsm_do_event(fsm,event);  


參考文獻:

技術系列之狀態機:http://www.cppblog.com/CppExplore/MyPosts.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章