天天看點

軟體實作狀态機要點

軟體實作狀态機要點

-v0.1 2019.5.3 Sherlock init

在比較複雜一點的軟體系統模組化的時候,有時需要理清楚系統的狀态。這個時候畫個系統

的狀态機出來就很有幫助。注意對于一個系統,可能同時又好幾個狀态機,之是以這樣,是

因為每個狀态機關注的主題是不一樣,每個獨立的狀态機隻能保證關注的主題邏輯上是自恰

的。

我們以一個例子說明下。假設我們要為一個裝置寫一個Linux核心驅動,在驅動裡這個裝置

被抽象為:

struct test_dev {
	int state;
	int a;
	int b;
	int c;
};
           

這個裝置需要初始化,然後配置下才能正常工作,這個裝置出錯的情況下需要複位這個裝置。

是以我們給出的test_dev這個裝置的狀态有:初始化狀态,正常工作狀态,複位狀态。在

判斷可能的狀态的時候,如果沒有必要就不要新增加狀态進來,一個狀态機裡每新增加一個

狀态以後都會成為負擔。

有了第一個狀态,就可以找可以進入這個狀态的激勵(event), 比如,這裡可能是:裝置初始化。

基于第一個狀态,分析在這個狀态可能接收到的所有激勵, 看看在這個狀态的test_dev接收

到某個激勵後對test_dev采取的動作(action)應該是怎麼樣的。比如這裡對于初始化狀态,

可能接收到的event可能有,1. 對test_dev配置,這個動作會使其進入正常工作狀态;2.

使用者還可以做初始化的逆操作,把這個裝置釋放掉。

重複對每一個狀态做上面的分析。我們可以大概得到一個這樣的狀态機:

軟體實作狀态機要點

接下來我們要針對每一個狀态分析是否還有其他的event會發生,比如,我們有可能發現

這個裝置驅動對應的fd檔案上有ioctl,那就需要分析在每一種狀态上,這個ioctl進來的

時候,test_dev是怎麼變化的。當然,在某種狀态的時候,我們是可以拒絕執行某種event

的。注意這裡分析的中心還是test_dev的變化,因為我們這裡要建立的是test_dev的狀态機,

我們不能脫離test_dev而去分析比如fd的變化。再比如,我們這個裝置驅動還mmap了一段

mmio空間到使用者态去,使用者态程式可以直接讀寫硬體寄存器,在test_dev的狀态機中也不應

該去考慮使用者态程式讀寫硬體寄存器這樣的event,因為test_dev的狀态機根本處理不了

這樣的情況。針對使用者态程式讀寫硬體寄存器這樣的event,我們要單獨分析。test_dev

狀态機需要考慮的是test_dev結構裡各個成員的變化。

完成對所有狀态在全部event下的分析,我們的狀态機就基本上建完了。剩下就是具體實作

的問題。實作中,要求所有對test_dev的action是原子的,也就是說狀态變遷的過程是不能

被打斷的,不然,可以看到我們會陷入各種各樣同步帶來的問題中。

還是看上面的圖,從初始狀态到正常工作狀态的切換如果不是原子的,那麼在這個過程中,

如果reset event來了,整個系統的狀态将如何切換?如果,configure action裡包括很多

步驟,而reset event可以在任何步驟到來,那麼這個切換的分析工作将變得非常複雜。

狀态切換變成原子操作将避免這樣的情況發生,使得整個系統的狀态變得可控。狀态切換

是原子行為時,如果test_dev正從初始狀态切換到工作狀态,那麼中間到來的reset event

将不得不排隊,之後再在一個明确的狀态響應reset event。

保證原子操作,一個簡單的辦法就是執行action的時候加鎖。

繼續閱讀