2013-08-21 54 views
1

問題定義:多線程嵌入式linux應用程序狀態機設計

我們正在爲運行Linux的工業嵌入式系統設計一個應用程序。

該系統由外部事件驅動。到系統的輸入可以是任何以下的:

  1. 很少輸入到系統中的數字IO線的形式(連接 像急停處理器的的GPIO)。
  2. 該系統運行一個網絡服務器,允許通過網絡瀏覽器控制系統爲 。
  3. 系統運行TCP服務器。任何PC或HMI設備都可以通過TCP/IP發送命令。

系統需要使用Modbus通過UART驅動或控制RS485從站設備。該系統還需要控制少數IO線路,如冷卻器開/關等。我們認爲,狀態機對於定義此應用程序至關重要。核心應用程序應是一個多線程應用程序,它應具備以下線程...

  1. 主線程
  2. 線程控制RS485奴隸。
  3. 線程處理來自Web界面的事件。
  4. 處理數字I/O事件的線程。
  5. 線程來處理的,我們使用的Pthread條件信號&等待通過TCP/IP(套接字)

對於線程間通信的命令。按照我們的初始設計方法(主線程中的一個狀態機),任何到系統的輸入事件(web或tcp/ip或數字I/O)都應該傳遞給主線程,並且應該與相應的線程進行通信該事件是註定的。典型的情況是通過Web界面獲取RS485從站的狀態。在這種情況下,Web界面線程應該將事件中繼給主線程,主線程將改變狀態,然後將事件傳送到控制RS485從站迴應的線程&。主線程應將響應發送回Web界面線程。

問題:

  1. 如果每個線程都有自己的狀態機,從而減少主線程的 複雜性?在這種情況下,我們是否仍然需要 在主線程中擁有狀態機?
  2. 任何線程處理輸入事件可以直接與 線程通信,該線程處理繞過主線程的事件?例如 網絡接口線程可以直接與線程 控制RS485從站通信?
  3. 使用pthread狀態信號是否正常&等待線程間 通信還是有更好的方法嗎?
  4. 如何讓一個線程等待來自外部的事件&響應 來自其他線程?對於例如Web界面線程通常會等待 處理POSIX消息隊列上的事件,以便進行來自Web服務器CGI bin的進程間通信 。 CGI bin通過這個消息隊列將接口線程發送到web 。處理此事件時,Web界面線程將等待來自其他線程的響應。在這種情況下,它無法處理來自網絡界面的任何新事件,直到它完成處理之前的 事件並返回到POSIX消息隊列的等待狀態。

抱歉太大的解釋......我希望我已經以最好的方式提出了我的解釋,讓別人理解和幫助我。

如果需要,我可以提供更多輸入。

+1

似乎原始問題中描述的方法重新發明活動對象(actor)設計模式。我建議看一下開源QP主動對象框架(http://www.state-machine.com/qp)。在http://www.state-machine.com/linux上有一個POS機的端口,帶有p-threads。 –

+0

當然,Mr.Miro先生,我會考慮這個選項......感謝一大堆 – hprasath

回答

3

我總是試圖處理這樣的需求是使用一個狀態機,由一個'SM'線程運行,這可能是主線程。這個線程在一個'EventQueue'輸入生產者 - 消費者隊列中等待超時。該超時用於運行內部增量隊列,在需要時可以向狀態機提供超時事件。

所有其他線程通過將消息推送到EventQueue來將其事件傳遞給狀態引擎,SM線程以串行方式處理它們。

如果SM中的一個動作例程決定它必須做某事,它不能同步等待任何事情,因此它必須通過將請求消息推送到任何線程/系統可執行的輸入隊列來請求動作。我的消息類(OK,* C中的struct)通常包含一個'command'枚舉,'result'枚舉,一個數據緩衝區指針(在需要傳輸批量數據的情況下),一個錯誤 - 消息指針,(如果沒有錯誤,則返回null),以及允許任何類型的請求異步排隊並返回完整結果(無論成功還是失敗)所需的其他狀態。

這個消息傳遞,一個SM設計是我發現的唯一一個能夠以靈活,可擴展的方式完成這些任務,而不會陷入惡夢世界的死鎖,不受控制的通信以及不可重複的不可逾越的交互。

關於任何設計應該問的第一個問題是'好的,如果有一些奇怪的問題,系統如何調試?'。在我上面的設計中,我可以直接回答:'我們記錄了在SM線程中出隊的所有事件 - 它們都是連續出現的,所以我們始終確切地知道基於它們採取了哪些操作。如果提出任何其他設計,請詢問上述問題,如果沒有立即提供好的答案,它將永遠無法工作。

所以:

  1. 如果一個線程,或螺紋子系統,可以使用單獨的狀態機來完成其自身的內部功能,OK,很好。這些SM應該從系統的其他部分看不見。

  2. 不!

  3. 使用pthread條件信號&等待實現生產者 - 消費者阻塞隊列。

  4. 每個線程/子系統一個輸入隊列。所有輸入都以消息的形式進入該隊列。每條消息中的命令/狀態標識消息以及應該如何處理。

BTW,我會100%做到這一點在C++中,除非獵槍在頭:)

+0

+1的可調試性。有見識的分解。 – andy256

+0

馬丁詹姆斯先生,我會考慮你提到的所有問題。我將不得不做一些工作,然後用新設計回覆你。謝謝.. – hprasath

+0

@Martin James,我在你的設計方法中有一些澄清。如果主狀態機或主線程將異步地向子系統線程分派輸入事件,主線程如何將結果作爲主線程將忙於等待輸入事件。我也不明白你提到的內部增量隊列嗎?你能詳細說明一下嗎? – hprasath

0

我已經實現了最初爲克隆西門子ES122C終端(EC115EC270)寫了一個傳統的嵌入式庫控制器。這個庫和操作系統或多或少包含你描述的內容。原來的硬件是基於80186 CPU。西門子的操作系統,RMOS和我們的FXMOS(不要谷歌它從未發佈過)擁有基本控制器工作所需的所有東西。它具有先佔式多任務處理,任務到任務通信,信號量,定時器和I/O事件,但沒有內存保護。 我把這些東西移植到RaspberryPi(即Linux)上。

我使用pthreads來模擬我們的遺留「任務」,因爲我們沒有內存保護,所以線程在語義上最接近。 其餘的實現繞過epoll API。這意味着所有產生事件的東西。事件發生時,計時器到期,另一個線程發送數據,連接TCP套接字,更改IO引腳狀態等......這要求所有事件源都在文件描述符中進行轉換。 Linux提供了幾個系統調用,它們的確如此: 用於任務任務通信我使用的是經典的Unix管道。 用於定時器事件我使用了timerfd API。用於TCP通信的 我使用普通套接字。 用於串口I/O的簡單打開,正確的設備/dev/???工程。 信號對我來說不是必需的,但Linux在必要時提供'signalfd'。我有epoll_wait纏繞來模擬原來的語義。

我的工作就像一個魅力。

TL; DR

走在epoll的API一個深沉的樣子它做什麼,你可能需要。

編輯:是的,馬丁詹姆斯的建議是非常好的,特別是4.每個線程應該只有在通過epoll_wait等待一個事件的循環。