歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux資訊 >> 更多Linux >> 面向實時MiniGUI體系結構之一體系結構概覽

面向實時MiniGUI體系結構之一體系結構概覽

日期:2017/2/27 9:29:47   编辑:更多Linux
  1 引言    到目前為止,MiniGUI 的最新發布版本是 0.9.96。我們將 0.9.xx 系列版本定位為 MiniGUI 1.0 版本的預覽版。在 0.9.xx 版本足夠穩定時,我們將發布 MiniGUI 1.0 版本,同時,目前的代碼不會再進行重大調整。在 MiniGUI 1.0 版本發布之後,我們將立即著手開發 MiniGUI 2.0 版本。該版本預期將在體系結構上進行重大調整。為了吸引更多的自由軟件程序員加入 MiniGUI 2.0 的開發,也為了更好地幫助 MiniGUI 程序員進行程序開發,我們將撰寫一系列的文章介紹 MiniGUI 1.0 版本的體系結構,重點分析其中的一些缺點以及需要在 2.0 版本當中進行優化和改造的地方。介紹體系結構的文章計劃如下:    體系結構概覽(本文)。將在整體上對 MiniGUI 1.0 的體系結構進行介紹。重點包括:線程的基本概念;多線程的微客戶/服務器體系、多線程通訊的關鍵數據結構――消息隊列;面向對象技術在 MiniGUI 中的應用等等。   MiniGUI 的多窗口管理。將介紹 MiniGUI 的多窗口機制以及相關的窗口類技術。其中涉及到窗口剪切處理和 Z 序,消息傳遞,控件類設計和輸入法模塊設計等等。   MiniGUI 的圖形設備管理。重點介紹 MiniGUI 是如何處理窗口繪制的。其中主要包括圖形上下文的概念,坐標映射,圖形上下文的局部、全局和有效剪切域的概念等等。   圖形抽象層和輸入抽象層。圖形抽象層(GAL)和輸入抽象層(IAL)大大提高了 MiniGUI 的可移植性,並將底層圖形設備和上層接口分離開來。這裡將重點介紹 MiniGUI 的 GAL 和 IAL 接口,並以 EP7211 等嵌入式系統為例,說明如何將 MiniGUI 移植到新的嵌入式平台上。   多字體和多字符集支持。MiniGUI 采用邏輯字體實現多字體和多字符集處理。這一技術成功應用了面向對象技術,通過單一的邏輯接口,可以實現對各種字符集以及各種字體的支持。   2 POSIX 線程    MiniGUI 是一個基於線程的窗口系統。為了理解 MiniGUI 的體系結構,我們有必要首先對線程作一番了解。    2.1 什麼是線程   線程通常被定義為一個進程中代碼的不同執行路線。也就是說,一個進程中,可以有多個不同的代碼路線在同時執行。例如,常見的字處理程序中,主線程處理用戶輸入,而其他並行運行的線程在必要時可在後台保存用戶的文檔。我們也可以說線程是“輕量級進程”。在 Linux 中,每個進程由五個基本的部分組成:代碼、數據、棧、文件I/O 和信號表。因此,系統對進程的處理要花費更多的開支,尤其在進行進程調度和任務切換時。從這個意義上,我們可以將一般的進程理解為重量級進程。在重量級進程之間,如果需要共享信息,一般只能采用管道或者共享內存的方式實現。如果重量級進程通過 fork() 派生了子進程,則父子進程之間只有代碼是共享的。    而我們這裡提到的線程,則通過共享一些基本部分而減輕了部分系統開支。通過共享這些基本組成部分,可以大大提高任務切換效率,同時數據的共享也不再困難――因為幾乎所有的東西都可以共享。    從實現方式上劃分,線程有兩種類型:“用戶級線程”和“內核級線程”。    用戶線程指不需要內核支持而在用戶程序中實現的線程,這種線程甚至在象 DOS 這樣的操作系統中也可實現,但線程的調度需要用戶程序完成,這有些類似 Windows 3.x 的協作式多任務。另外一種則需要內核的參與,由內核完成線程的調度。這兩種模型各有其好處和缺點。用戶線程不需要額外的內核開支,但是當一個線程因 I/O 而處於等待狀態時,整個進程就會被調度程序切換為等待狀態,其他線程得不到運行的機會;而內核線程則沒有各個限制,但卻占用了更多的系統開支。    Linux 支持內核級的多線程,同時,也可以從 Internet 上下載一些 Linux 上的用戶級的線程庫。Linux 的內核線程和其他操作系統的內核實現不同,前者更好一些。大多數操作系統單獨定義線程,從而增加了內核和調度程序的復雜性;而 Linux 則將線程定義為“執行上下文”,它實際只是進程的另外一個執行上下文而已。這樣,Linux 內核只需區分進程,只需要一個進程/線程數組,而調度程序仍然是進程的調度程序。Linux 的 clone 系統調用可用來建立新的線程。    2.2 POSIX 線程   POSIX 標准定義了線程操作的 C 語言接口。我們可以將 POSIX 線程的接口劃分如下:    線程的建立和銷毀。用來創建線程,取消線程,制造線程取消點等等。   互斥量操作接口。提供基本的共享對象互斥訪問機制。   信號量操作接口。提供基本的基於信號量的同步機制。不能與 System V IPC 機制的信號量相混淆。   條件量操作接口。提供基本的基於條件量的同步機制。盡管信號量和條件量均可以劃分為同步機制,但條件量比信號量更為靈活一些,比如可以進行廣播,設置等待超時等等。但條件量的操作比較復雜。   信號操作接口。處理線程間的信號發送和線程信號掩碼。   其他。包括線程局部存儲、一次性函數等等。   目前,Linux 上兼容 POSIX 的線程庫稱為 LinuxThreads,它已經作為 glibc 的一部分而發布。這些函數的名稱均以 pthread_ 開頭(信號量操作函數以 sem_ 開頭)。    為了對線程有一些感性認識,我們在這裡舉兩個例子。    第一個例子在進入 main () 函數之後,調用 pthread_create 函數建立了另一個線程。pthread_create 的參數主要有兩個,一個是新線程的入口函數(thread_entry),另一個是傳遞給入口函數的參數(data),而新線程的標識符通過引用參數返回(new_thread)。見清單 1。    清單 1 新線程的創建    void* thread_entry (void* data)  {   ...  // do something.   return NULL;  }    int main (void)  {    pthread_t new_thread;  int data = 2;    pthread_create (&new_thread, NULL, thread_entry, &data);  pthread_join (new_thread, NULL);  }    main () 函數在建立了新線程之後,調用 pthread_join 函數等待新線程執行結束。pthread_join 類似進程級的 wait 系統調用。當所等待的線程執行結束之後,該函數返回。利用 pthread_join 可用來實現一些簡單的線程同步。注意在上面的例子中,我們忽略了函數調用返回值的錯誤檢查。    第二個例子是利用信號量進行同步的兩個線程。這裡所使用的例子利用信號量解決了經典的“生產者/消費者”問題(清單 2)。我們首先解釋信號量的基本概念。    信號量的概念由 E. W. Dijkstra 於 1965 年首次提出。信號量實際是一個整數,進程(也可以是線程)在信號量上的操作分兩種,一種稱為 DOWN,而另外一種稱為 UP。DOWN 操作的結果是讓信號量的值減 1,UP 操作的結果是讓信號量的值加 1。在進行實際的操作之前,進程首先檢查信號量的當前值,如果當前值大於 0,則可以執行 DOWN 操作,否則進程休眠,等待其他進程在該信號量上的 UP 操作,因為其他進程的 UP 操作將讓信號量的值增加,從而它的 DOWN 操作可以成功完成。某信號量在經過某個進程的成功操作之後,其他休眠在該信號量上的進程就有可能成功完成自己的操作,這時,系統負責檢查休眠進程是否可以完成自己的操作。    為了理解信號量,我們想象某機票定購系統。最初旅客在定票時,一般有足夠的票數可以滿足定票量。當剩余的機票數為 1,而某個旅客現在需要定兩張票時,就無法滿足該顧客的需求,這時售票小姐讓這個旅客留下他的電話號碼,如果其他人退票,就可以優先讓這個旅客定票。如果最終有人退票,則售票小姐打電話通知上述要定兩張票的旅客,這時,該旅客就能夠定到自己的票。    我們可以將旅客看成是進程,而定票可看成是信號量上的 DOWN 操作,退票可看成是信號量上的 UP 操作,而信號量的初始值為機票總數,售票小姐則相當於操作系統的信號量管理器,由她(操作系統)決定旅客(進程)能不能完成操作,並且在新的條件成熟時,負責通知(喚醒)登記的(休眠的)旅客(進程)。    在操作系統中,信號量的最簡單形式是一個整數,多個進程可檢查並設置信號量的值。這種檢查並設置操作是不可被中斷的,也稱為“原子”操作。檢查並設置操作的結果是信號量的當前值和設置值相加的結果,該設置值可以是正值,也可以是負值。根據檢查和設置操作的結果,進行操作的進程可能會進入休眠狀態,而當其他進程完成自己的檢查並設置操作後,由系統檢查前一個休眠進程是否可以在新信號量值的條件下完成相應的檢查和設置操作。這樣,通過信號量,就可以協調多個進程的操作。    信號量可用來實現所謂的“關鍵段”。關鍵段指同一時刻只能有一個進程執行其中代碼的代碼段。也可用信號量解決經典的“生產者/消費者”問題,“生產者/消費者”問題和上述的定票問題類似。這一問題可以描述如下:    兩個進程共享一個公共的、固定大小的緩沖區。其中的一個進程,即生產者,向緩沖區放入信息,另外一個進程,即消費者,從緩沖區中取走信息(該問題也可以一般化為 m 個生產者和 n 個消費者)。當生產者向緩沖區放入信息時,如果緩沖區是滿的,則生產者進入休眠,而當消費者從緩沖區中拿走信息後,可喚醒生產者;當消費者從緩沖區中取信息時,如果緩沖區為空,則消費者進入休眠,而當生產者向緩沖區寫入信息後,可喚醒消費者。    清單 2 中的例子實際是“生產者/消費者”問題的線程版本。    清單 2 利用信號量解決“生產者/消費者”問題    /* The classic prodUCer-consumer example, implemented with semaphores.  All integers between 0 and 9999 should be printed exactly twi




Copyright © Linux教程網 All Rights Reserved