Windows CE 進程、線程和記憶體管理(一)

來源:互聯網
上載者:User
進程、線程、記憶體管理是一個核心最基本的服務,也是一個核心最主要的組成部分。這幾方面的知識是一個軟體開發人員必須掌握的基礎知識。雖然一個人不懂這些知識也能編寫簡單的程式,但這樣的程式只能算是皮毛。掌握了進程、線程和記憶體管理方面的知識,就能夠充分利用作業系統核心提供的服務,提高你編寫的軟體的執行效率、更節省資源、更健壯。順便說一下,在Windows CE.net下可以運行用Visual Studio.net開發的.net平台上啟動並執行軟體,但這樣的軟體是最上層的軟體,離作業系統核心太遠了。不但執行效率相對較低,而且還要把.net 架構加到核心中。所以在大多數情況下,EVC仍然是第一選擇。


圖一 CE核心結構

一、進程和線程

1、概念:
  Windows CE.NET是一個搶佔多任務作業系統,搶佔多任務又被稱為調度。在調度過程中,核心的調度系統包含一個當前所有進程中線程的優先順序列表,並對所有的線程按優先順序排列順序。當中斷髮生時,調度系統重新安排所有線程的排列順序。
  一個進程是一個正啟動並執行應用程式的執行個體。它由兩個部分組成:一個是作業系統用來管理這個進程的核心對象。另一個是這個進程擁有的地址空間。這個地址空間包含應用程式的程式碼片段、待用資料段、堆、棧,非XIP(Execute In Place)DLL。從執行角度方面看,一個進程由一個或多個線程組成。一個線程是一個執行單元,它控制CPU執行進程中某一段程式碼片段。一個線程可以訪問這個進程中所有的地址空間和資源。一個進程最少包括一個線程來執行代碼,這個線程又叫做主線程。

2、進程:
  Windows CE.NET最多支援32個進程同時運行。這是由整個系統分配給所有進程的總地址空間決定的。低於Windows CE 4.0版本(也就是低於.NET的版本)的CE作業系統,總進程空間從0x0000 0000到0x4200 0000 ,每32MB地址空間為一個槽(Slot),共33個槽。當一個進程啟動時,核心選擇一個沒有被佔用的槽作為這個進程的地址空間。其中0x0000 0000到0x01FF FFFF這個槽稱為Slot 0。每個進程在即將得到CPU控制權時,將整個地址映射到Slot 0。這個進程在協助文檔中稱為當前運行進程(currently running process)。分配一個槽後,核心在這個槽內按由低地址到高地址順序為程式碼片段、待用資料段分配足夠的地址空間,然後是堆、棧,棧之後的空間為所有 DLL保留,包括XIP和非XIP DLL。注意Slot 0最底部64KB是永遠保留的。從Slot 1 到 Slot32 為進程使用。前幾個槽一般為系統程式使用。如filesys.exe、device.exe、gwes.exe等。
  Windows CE.NET與低版本作業系統略有不同。這一點是從MSDN 的"Technical Articles"和"Knowledge Base"的文章中找到的,這的確讓我費了一番功夫。在Windows CE.NET的協助檔案中只能找到和早期版本相同的說法,而"Technical Articles"和"Knowledge Base"中有幾篇文章清楚的說明了Windows CE.NET 和低版本作業系統的不同。在低版本作業系統中,的確如上所說分為33個槽,Slot 0用於當前運行進程,共支援32個進程同時運行。而且所有DLL都載入到進程的地址空間。但Windows CE.NET下 Slot 1也用於當前進程(Slot 1隻用於載入所有XIP DLL)。那麼一個進程就不是佔有32MB地址空間了,而是64MB。在講解記憶體管理時我會具體講解。
建立一個進程的API函數如下:

BOOL  CreateProcess(LPCTSTR  lpApplicationName, LPTSTR  lpCommandLine,LPSECURITY_ATTRIBUTES  lpProcessAttributes,LPSECURITY_ATTRIBUTES  lpThreadAttributes,BOOL  bInheritHandles,DWORD  dwCreationFlags,LPVOID  lpEnvironment,LPCTSTR  lpCurrentDirectory,LPSTARTUPINFO  lpStartupInfo,LPPROCESS_INFORMATION  lpProcessInformation );

  Windows CE.NET不支援安全性、目前的目錄、繼承性,所以這個函數很多參數都必須設為0或FALSE。具體第3、4、7、8、9設為0,第5設為FALSE。第1參數為應用程式名稱,這個參數不能為NULL。如果只傳遞應用程式名稱而沒有指定路徑,那麼系統將先搜尋\Windows目錄,接著搜尋OEM指定的搜尋路徑。第2參數用於傳遞啟動參數,必須為UNICODE碼。第6參數為建立標誌。可以為0(建立一個常規進程)、CREATE_SUSPENDED(啟動後掛起)、DEBUG_PROCESS(用於建立這個進程的父進程調試用)、DEBUG_ONLY_THIS_PROCESS(不調試子進程)、CREATE_NEW_CONSOLE(控制台進程)。第10參數傳遞給它一個PROCESS_INFORMATION結構變數的地址。返回進程和主線程的控制代碼和ID。
  終止一個進程最好是由WinMain函數返回。在主線程中調用ExitThread函數也可以。在當前進程終止另一個進程使用TerminateProcess函數。CE下的TerminateProcess函數要比其他Windows下TerminateProcess函數功能強大。CE下的TerminateProcess函數在使進程退出時,會通知每個載入的DLL並做出進程退出時該做的所有處理工作。

3、線程:
  線程除了能夠訪問進程的資源外,每個線程還擁有自己的棧。棧的大小是可以調整的,最小為1KB或4KB(也就是一個記憶體頁。記憶體頁的大小取決於CPU),一般預設為64KB,但棧頂端永遠保留2KB為防止溢出。如果要改變棧初始時大小,在EVC"Project"-"Settings"-"Link"連結選項"/STACK"後的參數中指定大小。其中參數1為預設大小,參數2為一個記憶體頁大小,都用十六進位表示。如果將棧的初始值設定太小,很容易導致系統訪問非法並立即終止進程。
  線程有五中狀態,分別為運行、掛起、睡眠、阻塞、終止。當所有線程全部處於阻塞狀態時,核心處於空閑模式(Idle mode),這時對CPU的電力供應將減小。
建立一個線程的API函數如下:

HANDLE  CreateThread(LPSECURITY_ATTRIBUTES  lpThreadAttributes,DWORD  dwStackSize,LPTHREAD_START_ROUTINE  lpStartAddress,LPVOID  lpParameter,DWORD  dwCreationFlags,LPDWORD  lpThreadId );      

Windows CE.NET 不支援安全性所以參數1必須設定為0。如果參數5為STACK_SIZE_PARAM_IS_A_RESERVATION,那麼參數2可以指定棧的大小,核心將按照參數2的數值來為此線程擁有的棧保留地址空間。如果參數5不為STACK_SIZE_PARAM_IS_A_RESERVATION,那麼參數2必須設定為0。參數3為執行路徑的首地址,也就是函數的地址。參數4用來向線程中傳遞一個參數。參數5除了上面說明外,還可以為0、CREATE_SUSPENDED。CREATE_SUSPENDED表示這個線程在建立後一直處於掛起狀態,直到用ResumeThread函數來恢複。最後一個參數儲存函數返回的建立的線程ID。
  退出一個線程同退出一個進程有類似的方法。最好是由函數返回,線上程中調用ExitThead函數也可以。在當前線程中終止另一個線程使用TerminateThread函數。此函數在使一個線程退出時,會通知這個線程載入的所有DLL。這樣DLL就可以做結束工作了。
  Windows CE.NET不像其他Windows作業系統將進程分為不同的優先順序類,Windows CE.NET只將線程分為256個優先順序。0優先順序最高,255最低,0到248優先順序屬於即時性優先順序。0到247優先順序一般分配給即時性應用程式、驅動程式、系統程式。249到255優先順序中,251優先順序(THREAD_PRIORITY_NORMAL)是正常優先順序。255優先順序(THREAD_PRIORITY_IDLE)為空白閑優先順序。249優先順序(THREAD_PRIORITY_HIGHEST)是高優先順序。248到255優先順序一般分配給普通應用程式線程使用。具體分段見下表:
 

優先順序範圍 指派至
0-96 高於驅動程式的程式
97-152 基於Windows CE的驅動程式
153-247 低於驅動程式的程式
248-255 普通的應用程式

  Windows CE.NET作業系統具有即時性,所以調度系統必須保證高優先順序線程先運行,低優先順序線程在高優先順序線程終止後或者阻塞時才能得到CPU時間片。而且一旦發生中斷,核心會暫停低優先順序線程的運行,讓高優先順序線程繼續運行,直到終止或者阻塞。具有相同優先順序的線程平均佔有CPU時間片,當一個線程使用完了CPU時間片或在時間片內阻塞、睡眠,那麼其他相同優先順序的線程會佔有時間片。這裡提到的CPU時間片是指核心限制線程佔有CPU的時間,預設為100ms。OEM可以更改這個值,甚至設定為0。如果為0,當前線程將一直佔有CPU,直到更高優先順序線程要求佔有CPU。這個調度演算法好像是很有效、很完美,但卻存在著一種情況,當這種情況發生時程式會死結。舉例來說:一個應用程式套件組合含兩個線程,線程1是高優先順序,線程2是低優先順序,當線程1運行過程中處於阻塞時,線程2得到時間片,線程2這次進入了一個臨界區,我們都知道臨界區內的資源是不會被其它線程訪問的,當線程2正運行時,線程1已經從阻塞狀態轉變為運行狀態,而這次線程1卻要訪問線程2的資源,這個資源卻被臨界區鎖定,那麼線程1隻能等待,等待線程2從臨界區中運行結束並釋放資源的獨佔權。但是線程2卻永遠不會得到時間片,因為CE保證高優先順序線程會先運行。這時程式就會處於死結狀態。當然系統不會死結,因為還有更高優先順序的線程、驅動程式在運行。對於這種情況,CE採取優先順序轉換的辦法來解決。就是當發生這種情況時,核心將線程2的優先順序提高到線程1的優先順序水平。這樣線程2就可以執行完臨界區代碼了,線程1也就能夠訪問資源了。然後核心再恢複線程2原來的優先順序。
  掛起一個線程使用SuspendThread函數。參數只有一個――線程的控制代碼。要說明的是如果要掛起的線程正調用一個核心功能,這時執行此函數可能會失敗。需要多次調用此函數直到函數傳回值不為0xFFFFFFFF,說明掛起成功。恢複線程使用ResumeThread函數。參數也只有一個――線程的控制代碼。
  關於執行緒區域儲存空間和纖程,實際用到的時候非常少,這部分知識可以參考《Windows核心編程》。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.