熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Oracle >> 正文

基於 Linux 和 MiniGUI 的嵌入式系統軟件開發指南(六)

2013-11-13 16:13:00  來源: Oracle 

  作者魏永明
  
  MiniGUI 提供的非 GUI/GDI 接口
  
  本文講述了 MiniGUI 為應用程序提供的非 GUI/GDI 接口這些接口能夠幫助應用程序更好地和操作系統交互擴展應用程序功能並提高應用程序的可移植性內容主要涉及到如下幾個方面MiniGUILite 和 select 系統調用基於 UNIX Domain Socket 的進程間通訊編寫可移植性代碼等
   引言
  一般而言GUI 系統的應用程序編程接口主要集中於窗口消息隊列圖形設備等相關方面但因為 GUI 系統在處理系統事件時通常會提供自己的機制而這些機制往往會和操作系統本身提供的機制不相兼容比如MiniGUI 提供了消息循環機制而應用程序的結構一般是消息驅動的也就是說應用程序通過被動接收消息來工作但很多情況下應用程序需要主動監視某個系統事件比如在 UNIX 操作系統中可以通過 select 系統調用監聽某個文件描述符上是否有可讀數據這樣如何將 MiniGUI 的消息隊列機制和現有操作系統的其他機制融合在一起就成了一個較為困難的問題本文將講述幾種解決這一問題的方法
  
  我們知道MiniGUILite 采用 UNIX Domain Socket 實現客戶程序和服務器程序之間的交互應用程序也可以利用這一機制完成自己的通訊任務――客戶向服務器提交請求而服務器完成對客戶的請求處理並應答一方面在 MiniGUILite 的服務器程序中你可以擴展這一機制注冊自己的請求處理函數完成定制的請求/響應通訊任務另一方面MiniGUILite 當中也提供了若干用來創建和操作 UNIX Domain Socket 的函數任何 MiniGUILite 的應用程序都可以建立 UNIX Domain Socket並完成和其他 MiniGUILite 應用程序之間的數據交換本文將舉例講述如何利用 MiniGUILite 提供的函數完成此類通訊任務
  
  嵌入式 Linux 系統現在能夠在許多不同架構的硬件平台上運行MiniGUI 也能夠在這些硬件平台上運行但由於許多硬件平台具有和其他硬件平台不同的特性比如說常見的 CPU 是 Little Endian 的而某些 CPU 則是 Big Endian 的這要求我們在編寫代碼尤其是文件 I/O 相關代碼時必須編寫可移植代碼以便適合具有不同架構的平台本文將描述 MiniGUI 為應用程序提供的可移植性函數及其用法
  
  除了與上述內容相關的函數之外MiniGUI 還提供了其他一些函數本文最後部分將描述這些函數的用途和用法包括配置文件讀寫以及定點數運算
  
   MiniGUILite和 select 系統調用
  我們知道在 MiniGUILite 之上運行的應用程序只有一個消息隊列應用程序在初始化之後會建立一個消息循環然後不停地從這個消息隊列當中獲得消息並處理直到接收到 MSG_QUIT 消息為止應用程序的窗口過程在處理消息時要在處理完消息之後立即返回以便有機會獲得其他的消息並處理現在如果應用程序在處理某個消息時監聽某個文件描述符而調用 select 系統調用就有可能會出現問題――因為 select 系統調用可能會長時間阻塞而由 MiniGUILite 服務器發送給客戶的事件得不到及時處理這樣消息驅動的方式和 select 系統調用就難於很好地融合在 MiniGUIThreads 中因為每個線程都有自己相應的消息隊列而系統消息隊列是由單獨運行的 desktop 線程管理的所以任何一個應用程序建立的線程都可以長時間阻塞從而可以調用類似 select 的系統調用但在 MiniGUILite 當中如果要監聽某個應用程序自己的文件描述符事件必須進行恰當的處理以避免長時間阻塞
  
  在 MiniGUILite 當中有幾種解決這一問題的辦法
  
  在調用 select 系統調用時傳遞超時值保證 select 系統調用不會長時間阻塞
  設置定時器定時器到期時利用 select 系統調用查看被監聽的文件描述符如果沒有相應的事件發生則立即返回否則進行讀寫操作
  利用 MiniGUILite 提供的 RegisterListenFD 函數在系統中注冊監聽文件描述符並在被監聽的文件描述符上發生指定的事件時向某個窗口發送 MSG_FDEVENT 消息
  
  由於前兩種解決方法比較簡單這裡我們重點講述的第三種解決辦法MiniGUILite 為應用程序提供了如下兩個函數及一個宏
  #define MAX_NR_LISTEN_FD
  
  /* Return TRUE if all OK and FALSE on error */
  BOOL GUIAPI RegisterListenFD (int fd int type HWND hwnd void* context);
  
  /* Return TRUE if all OK and FALSE on error */
  BOOL GUIAPI UnregisterListenFD (int fd);
  
  MAX_NR_LISTEN_FD 宏定義了系統能夠監聽的最多文件描述符數默認定義為
  RegisterListenFD 函數在系統當中注冊一個需要監聽的文件描述符並指定監聽的事件類型(type 參數可取 POLLINPOLLOUT 或者 POLLERR)接收 MSG_FDEVENT 消息的窗口句柄以及一個上下文信息
  UnregisterListenFD 函數注銷一個被注冊的監聽文件描述符
  
  在應用程序使用RegisterListenFD 函數注冊了被監聽的文件描述符之後當指定的事件發生在該文件描述符上時系統會將 MSG_FDEVENT 消息發送到指定的窗口應用程序可在窗口過程中接收該消息並處理MiniGUI 中的 libvcongui 就利用了上述函數監聽來自主控偽終端上的可讀事件如下面的程序段所示(vcongui/vconguic)
  
  
   /* 注冊主控偽終端偽監聽文件描述符 */
   RegisterListenFD (pConInfo>masterPty POLLIN hMainWnd );
  
   /* 進入消息循環 */
   while (!pConInfo>terminate && GetMessage (&Msg hMainWnd)) {
   DispatchMessage (&Msg);
   }
   /* 注銷監聽文件描述符 */
   UnregisterListenFD (pConInfo>masterPty);
  
  
  
  /* 虛擬控制台的窗口過程 */
  static int VCOnGUIMainWinProc (HWND hWnd int message WPARAM wParam LPARAM lParam)
  {
   PCONINFO pConInfo;
  
   pConInfo = (PCONINFO)GetWindowAdditionalData (hWnd);
   switch (message) {
  
  
  
   /* 接收到 MSG_FDEVENT 消息則處理主控偽終端上的輸入數據 */
   case MSG_FDEVENT:
   ReadMasterPty (pConInfo);
   break;
  
  
   }
  
   /* 調用默認窗口過程 */
   if (pConInfo>DefWinProc)
   return (*pConInfo>DefWinProc)(hWnd message wParam lParam);
   else
   return DefaultMainWinProc (hWnd message wParam lParam);
  }
  
  在 節當中我們還可以看到RegisterListenFD 函數的使用顯然通過這種簡單的注冊監聽文件描述符的接口MiniGUILite 程序能夠方便地利用底層的消息機制完成對異步事件的處理
  
   MiniGUILite 與進程間通訊
  
   簡單請求/應答處理
  我們知道MiniGUILite 利用了 UNIX Domain Socket 實現服務器和客戶程序之間的通訊為了實現客戶和服務器之間的簡單方便的通訊MiniGUILite 中定義了一種簡單的請求/響應結構客戶程序通過指定的結構將請求發送到服務器服務器處理請求並應答在客戶端一個請求定義如下(include/gdih)
  typedef struct tagREQUEST {
   int id;
   const void* data;
   size_t len_data;
  } REQUEST;
  typedef REQUEST* PREQUEST;
  
  其中id 是用來標識請求類型的整型數data 是發送給該請求的關聯數據len_data 則是數據的長度客戶在初始化 REQUEST 結構之後就可以調用 cli_request 向服務器發送請求並等待服務器的應答該函數的原型如下
  /* send a request to server and wait reply */
  int cli_request (PREQUEST request void* result int len_rslt);
  
  
  
  
  服務器程序(即 mginit)會在自己的消息循環當中獲得來自客戶的請求並進行處理最終會將處理結果發送給客戶
  
  在上述這種簡單的客戶/服務器通訊中客戶和服務器必須就每個請求類型達成一致也就是說客戶和服務器必須了解每種類型請求的數據含義並進行恰當的處理
  
  MiniGUILite 利用上述這種簡單的通訊方法實現了若干系統級的通訊任務
  
  鼠標光標的管理鼠標光標是一個全局資源當客戶需要創建或者銷毀鼠標光標改變鼠標光標的形狀位置顯示或者隱藏鼠標時就發送請求到服務器服務器程序完成相應任務並將結果發送給客戶
  層及活動客戶管理當客戶查詢層的信息新建層加入某個已有層或者設置層中的活動客戶時通過該接口發送請求到服務器
  其他一些系統級的任務比如在新的 GDI 接口中服務器程序統一管理顯示卡中可能用來建立內存 DC 的顯示內存當客戶要申請建立在顯示內存中的內存 DC 時就會發送請求到服務器
  
  為了讓應用程序也能夠通過這種簡單的方式實現客戶和服務器之間的通訊服務器程序可以注冊一些定制的請求處理函數然後客戶就可以向服務器發送這些請求為此MiniGUILite 提供了如下接口
  #define MAX_SYS_REQID x
  #define MAX_REQID x
  
  /*
   * Register user defined request handlers for server
   * Note that user defined request id should larger
From:http://tw.wingwit.com/Article/program/Oracle/201311/17973.html
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.