操作系統(tǒng)實驗報告_第1頁
操作系統(tǒng)實驗報告_第2頁
操作系統(tǒng)實驗報告_第3頁
操作系統(tǒng)實驗報告_第4頁
操作系統(tǒng)實驗報告_第5頁
已閱讀5頁,還剩31頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

1、操作系統(tǒng)實驗報告實驗一 線程的狀態(tài)和轉換(5分)1 實驗目的和要求目的:熟悉線程的狀態(tài)及其轉換,理解線程狀態(tài)轉換與線程調度的關系。要求:(1)跟蹤調試EOS線程在各種狀態(tài)間的轉換過程,分析EOS中線程狀態(tài)及其轉換的相關源代碼;(2)修改EOS的源代碼,為線程增加掛起狀態(tài)。2 完成的實驗內(nèi)容2.1 EOS線程狀態(tài)轉換過程的跟蹤與源代碼分析(分析EOS中線程狀態(tài)及其轉換的核心源代碼,說明EOS定義的線程狀態(tài)以及狀態(tài)轉換的實現(xiàn)方法;給出在本部分實驗過程中完成的主要工作,包括調試、跟蹤與思考等)實驗主要分析EOS的下列線程狀態(tài)轉換:線程由阻塞狀態(tài)進入就緒狀態(tài)。線程由運行狀態(tài)進入就緒狀態(tài)。線程由就緒狀態(tài)

2、進入運行狀態(tài)。線程由運行狀態(tài)進入阻塞狀態(tài)。Loop函數(shù):系統(tǒng)準備了控制臺命令“l(fā)oop”,命令函數(shù)是ke/sysproc.c文件中ConsoleCmdLoop函數(shù)(Line 797),此函數(shù)中使用LoopThreadFunction創(chuàng)建優(yōu)先級為8的線程。在線程轉換實驗中,loop線程的主要作用是表示線程的狀態(tài),如運行、阻塞、掛起、就緒、恢復狀態(tài)。loop線程代碼分析如下:LoopThreadFunction(PVOID Param) ULONG i;ULONG ThreadID = GetCurrentThreadId();COORD CursorPosition;HANDLE StdHand

3、le = (HANDLE)Param;for (i = 0; i < 24; i+) fprintf(StdHandle, "n"); / 清理整個屏幕的內(nèi)容。CursorPosition.X = 0; / 設置線程輸出內(nèi)容顯示的位置CursorPosition.Y = 0;for (i=0;i+) / 死循環(huán)。SetConsoleCursorPosition(StdHandle, CursorPosition);fprintf(StdHandle, "Loop thread ID %d : %u ", ThreadID, i); / 格式:Thr

4、ead ID 線程ID : 執(zhí)行計數(shù)return 0;分析:CreateThread用于創(chuàng)建線程,第一個“0”對應于LPSECURITY_ATTRIBUTES,即對象描述符,這個結構為很多函數(shù)創(chuàng)建對象是提供安全性設置,第二個“0”和“NULL”為CreateThread的配置參數(shù)。LoopThreadFunction是創(chuàng)建loop循環(huán)線程,在這個函數(shù)中用于在控制臺顯示的代碼在787行的循環(huán)函數(shù).2.1.1線程由阻塞狀態(tài)進入就緒狀態(tài)通過實驗步驟的調試,可以將線程由阻塞狀態(tài)進入就緒狀態(tài)的步驟總結如下:將線程從等待隊列中移除 將線程的狀態(tài)由 Waiting 修改為 Zero將線程插入其優(yōu)先級對應的就

5、緒隊列的隊尾將線程的狀態(tài)由 Zero 修改為 Ready狀態(tài)轉換過程中使用到的函數(shù)為PspUnwaitThread函數(shù)和PspWakeThread 函數(shù),其中PspUnwaitThread函數(shù)是使處于等待狀態(tài)的線程脫離等待隊列并轉入Zero狀態(tài),即使線程脫離阻塞狀態(tài)。PspWakeThread函數(shù)是改變線程的轉臺值,使Zero狀態(tài)或者運行狀態(tài)的線程轉入就緒狀態(tài)。從函數(shù)代碼分析轉換的功能PspUnwaitThread函數(shù)VOID PspUnwaitThread( IN PTHREAD Thread )/功能描述:使處于等待狀態(tài)的線程脫離等待隊列并轉入 Zero 狀態(tài)。/參數(shù): Thread -

6、目標線程對象指針。ASSERT(Waiting = Thread->State); / 將線程從所在等待隊列中移除并修改狀態(tài)碼為Zero。ListRemoveEntry (&Thread->StateListEntry);Thread->State = Zero; / 如果線程注冊了等待計時器,則注銷等待計時器。if (STATUS_TIMEOUT = Thread->WaitStatus) KeUnregisterTimer(&Thread->WaitTimer);分析:函數(shù)的參數(shù)Thread是目標線程對象指針,執(zhí)行如下。ASSERT(Waiti

7、ng = Thread->State);首先測試線程是不是在等待隊列,如果不是則結束函數(shù)的執(zhí)行,在程序運行時,主要用于調試,使其不容易出現(xiàn)錯誤或異常。ListRemoveEntry(&Thread->StateListEntry);Thread->State = Zero;將線程從所在等待隊列中移除并修改狀態(tài)碼為Zero。其中&Thread->StateListEntry是線程隊列,ListRemoveEntry函數(shù)是移除等待隊列的堆棧,第二句是使線程狀態(tài)修改為Zero。PspWakeThread函數(shù)PspWakeThread(IN PLIST_ENTR

8、Y WaitListHead,IN STATUS WaitStatus) /功能描述:喚醒指定等待隊列的隊首線程。/參數(shù): WaitListHead - 等待隊列指針。 /WaitStatus - 被喚醒線程從PspWait返回的返回值。 PTHREAD Thread;If (! ListIsEmpty (WaitListHead) / 喚醒等待隊列的隊首線程。Thread = CONTAINING_RECORD (WaitListHead->Next, THREAD, StateListEntry);PspUnwaitThread (Thread);PspReadyThread (Th

9、read);Thread->WaitStatus = WaitStatus; / 設置線程從PspWait返回的返回值。 else Thread = NULL ;return Thread;分析: 函數(shù)中使處于等待隊列中的線程的狀態(tài)發(fā)生改變的函數(shù)是“PspReadyThread (Thread)”,功能是使 Zero 狀態(tài)或者運行狀態(tài)的線程轉入就緒狀態(tài)。與PspUnwaitThread函數(shù)聯(lián)合使用,從而完成線程從阻塞到就緒的狀態(tài)改變。2.1.2線程由運行狀態(tài)進入就緒狀態(tài) 通過試驗調試,可以將線程由運行狀態(tài)進入就緒狀態(tài)的步驟總結如下:(1) 線程中斷運行,將線程中斷運行時的上下文保存到線程

10、控制塊中。(2) 如果處于運行狀態(tài)的線程被更高優(yōu)先級的線程搶先,就需要將該線程插入其優(yōu)先級對應的就緒隊列的隊首。(3) 將線程的狀態(tài)由 Running 修改為 Ready。狀態(tài)轉換在PspSelectNextThread函數(shù)中完成,主要分為兩部分,被中斷的線程處于運行狀態(tài)和非運行狀態(tài),代碼分析如下:運行狀態(tài):if (NULL != PspCurrentThread && Running = PspCurrentThread->State)if (0 != PspReadyBitmap && HighestPriority > PspCurrentTh

11、read->Priority) / 如果存在比當前運行線程優(yōu)先級更高的就緒線程,當前線程應被搶先。/ 因為當前線程仍處于運行狀態(tài),所以被高優(yōu)先級線程搶先后應插入其/ 優(yōu)先級對應的就緒隊列的隊首。ListInsertHead (&PspReadyListHeads PspCurrentThread->Priority,&PspCurrentThread->StateListEntry);BIT_SET (PspReadyBitmap, PspCurrentThread->Priority);PspCurrentThread->State = Read

12、y; else / 當前線程繼續(xù)運行。MmSwapProcessAddressSpace (PspCurrentThread->Attached as);return &PspCurrentThread->KernelContext;分析:第一句判斷當前非空進程的狀態(tài)是否是運行,是的話執(zhí)行下面過程;后一個if語句比較運行進程的優(yōu)先級,如果有等高的優(yōu)先級的進程進行申請,該正在運行的進程被中斷,狀態(tài)改為就緒(ready),轉換過程在else上一句執(zhí)行。非運行狀態(tài):if (0 = PspReadyBitmap) / 被中斷運行線程處于非運行狀態(tài),必須存在一個可運行的就緒線程。AS

13、SERT (FALSE);KeBugCheck ("No ready thread to run!");分析:當前被中斷的進程處于非運行狀態(tài)時,要么使其運行,要么等待可運行的就緒進程。2.1.3線程由運行狀態(tài)進入就緒狀態(tài)將線程從其優(yōu)先級對應的就緒隊列中移除將線程的狀態(tài)由 Ready 修改為 Zero將線程的狀態(tài)由 Zero 修改為 Running將線程的上下文從線程控制塊(TCB)復制到處理器的各個寄存器中,讓線程從上次停止運行的位置繼續(xù)運行。VOIDPspUnreadyThread( PTHREAD Thread )/功能描述:取消線程的就緒狀態(tài),使線程轉入 Zero 狀

14、態(tài)。/參數(shù): Thread - 當前處于就緒狀態(tài)的線程的指針。ASSERT(NULL != Thread && Ready = Thread->State);/ 將線程從所在的就緒隊列中取出,如果線程優(yōu)先級對應的就緒隊列變?yōu)榭眨?/ 則清除就緒位圖中對應的位。/ListRemoveEntry(&Thread->StateListEntry);if(ListIsEmpty(&PspReadyListHeadsThread->Priority) BIT_CLEAR(PspReadyBitmap, Thread->Priority);Threa

15、d->State = Zero;分析:函數(shù)將線程狀態(tài)轉入就緒,使其狀態(tài)為zero,再轉回PspSelectNextThread將函數(shù)狀態(tài)值設為2,即運行狀態(tài)。2.1.4線程由由運行狀態(tài)進入阻塞狀態(tài)將線程插入等待隊列的隊尾。將線程的狀態(tài)由 Running 修改為 Waiting將線程中斷執(zhí)行,并將處理器上下文保存到該線程的線程控制塊中。PspWait(IN PLIST_ENTRY WaitListHead,IN ULONG Milliseconds) WaitStatus。ASSERT(0 = KeGetIntNesting();ASSERT(Running = PspCurrentThr

16、ead->State);ASSERT(0 != PspReadyBitmap);if(0 = Milliseconds) return STATUS_TIMEOUT;/ 將當前線程插入等待隊列的隊尾并修改線程狀態(tài)碼為Waiting。ListInsertTail(WaitListHead, &PspCurrentThread->StateListEntry);PspCurrentThread->State = Waiting;/ 如果不是永久等待,就注冊一個用于超時喚醒線程的等待計時器。if (INFINITE != Milliseconds) KeInitialize

17、Timer( &PspCurrentThread->WaitTimer, Milliseconds, PspOnWaitTimeout, (ULONG_PTR)PspCurrentThread );KeRegisterTimer(&PspCurrentThread->WaitTimer);PspCurrentThread->WaitStatus = STATUS_TIMEOUT; else PspCurrentThread->WaitStatus = STATUS_SUCCESS;/ 當前線程進入等待狀態(tài)后需要讓出處理器(讓權等待),執(zhí)行線程調度。Psp

18、ThreadSchedule();/ 線程被喚醒繼續(xù)執(zhí)行,返回等待結果狀態(tài)碼。return PspCurrentThread->WaitStatus; 分析:線程由運行狀態(tài)改為阻塞狀態(tài),相應的狀態(tài)值應該由”2”改為”0”,在上面的代碼中, “PspCurrentThread->State = Waiting”是完成這一轉換功能的。2.2為線程增加掛起狀態(tài)的實現(xiàn)(給出實現(xiàn)方法的簡要描述、源代碼、測試和結果等)(1)添加代碼的原理:首先調用 ListRemoveEntry 函數(shù)將線程從掛起線程隊列中移除,然后調用 PspReadyThread 函數(shù)將線程恢復為就緒狀態(tài),最后調用 Psp

19、ThreadSchedule 宏函數(shù)執(zhí)行線程調度,讓剛剛恢復的線程有機會執(zhí)行。(2)源代碼:STATUSPsResumThread ( IN HANDLE hThread )/功能描述:恢復指定的線程。/參數(shù): hThread - 需要被恢復的線程的句柄。/返回值:如果成功則返回 STATUS_SUCCESS。STATUS Status;BOOL IntState;PTHREAD Thread;/ 根據(jù)線程句柄獲得線程對象的指針/Status = ObRefObjectByHandle(hThread, PspThreadType, (PVOID*)&Thread);if (EOS_S

20、UCCESS(Status) IntState = KeEnableInterrupts(FALSE);/ 關中斷if (Zero = Thread->State) / 在此添加代碼將線程恢復為就緒狀態(tài) ListRemoveEntry(&Thread->StateListEntry); PspReadyThread(Thread); PspThreadSchedule();/添加完成Status = STATUS_SUCCESS; else Status = STATUS_NOT_SUPPORTED;KeEnableInterrupts(IntState);/ 開中斷ObD

21、erefObject(Thread);return Status;分析:本實驗添加了三行代碼,實際測試時,只添加中間的函數(shù)“PspReadyThread”,resume仍然正常執(zhí)行,原因是只有一個進程從就緒列表中取出,若是連續(xù)兩個進程先執(zhí)行suspend,則會出錯,所以嚴謹?shù)膶懛☉亚昂髢尚写a加上。ListRemoveEntry用于將進程從掛起狀態(tài)移除,PspThreadSchedule()調用此進程。(3)測試: 首先調用loop循環(huán)進程,在并行的控制臺中輸入suspend 進程號,返回原控制臺觀察現(xiàn)象,然后執(zhí)行resume 進程號,再返回控制臺查看,并輸出當前進程所有的進程的狀態(tài)信息。(

22、4)測試結果: 進程狀態(tài)信息:ready為就緒,waiting為阻塞,running為執(zhí)行3 其他需要說明的問題 3.1思考一下,在本實驗中,當 loop 線程處于運行狀態(tài)時,EOS 中還有哪些線程,它們分別處于什么狀態(tài)??梢允褂每刂婆_命令 pt 查看線程的狀態(tài)。 答:如上圖所示,2號進程處于就緒狀態(tài),2028處于掛起狀態(tài),31運行 3.2當 loop 線程在控制臺 1 中執(zhí)行,并且在控制臺 2 中執(zhí)行 suspend 命令時,為什么控制臺 1 中的 loop線程處于就緒狀態(tài)而不是運行狀態(tài)?答:打開控制臺3時(控制臺2在本實驗中不能使用),loop線程處于活動就緒狀態(tài),此時在控制臺3中使用掛起

23、原語Suspend可以將該線程掛起,該線程便會轉入靜止就緒狀態(tài)。處于靜止就緒狀態(tài)的線程,不會再被調度執(zhí)行,直到使用原語Resume將該線程恢復為活動就緒狀態(tài)。3.3總結一下在圖 5-3 中顯示的轉換過程,哪些需要使用線程控制塊中的上下文(將線程控制塊中的上下文恢復到處理器中,或者將處理器的狀態(tài)復制到線程控制塊的上下文中),哪些不需要使用,并說明原因。答:新建->就緒 :當創(chuàng)建一個進程或線程時,新進程的主線程或者新線程都會被初始化為就緒狀態(tài),并被放入就緒隊列中。就緒->運行 :當調度程序認為某個處于就緒狀態(tài)的線程應當執(zhí)行時,便使其成為當前運行的線程,該線程就會從就

24、緒狀態(tài)進入運行狀態(tài)。 運行->就緒 :當前運行線程因時間片用完或被更高優(yōu)先級線程搶先時,當前運行線程就會由運行狀態(tài)轉入就緒狀態(tài)。 運行->阻塞 :當前運行線程可能因調用API函數(shù)WaitForSingleObject等待事件或者執(zhí)行I/O請求而被阻塞,從而由運行狀態(tài)轉入阻塞狀態(tài)。 阻塞->就緒 :處于阻塞狀態(tài)的線程所等待的事件變?yōu)橛行Ш?,或等待超時后,該線程將被喚醒,從而由阻塞狀態(tài)進入就緒狀態(tài)。 任意狀態(tài)->結束狀態(tài) 線程可以由任意一個狀態(tài)轉入結束狀態(tài)。例如,線程執(zhí)行完畢會由運行狀態(tài)轉入結束

25、狀態(tài),就緒線程或者阻塞線程如果被強制結束,也會轉入結束狀態(tài)。3.4. 在本實驗 3.2 節(jié)中總結的所有轉換過程都是分步驟進行的,為了確保完整性,顯然這些轉換過程是不應該被打斷的,也就是說這些轉換過程都是原語操作(參見本書第 2.6 節(jié))。請讀者找出這些轉換過程的原語操作(關中斷和開中斷)是在哪些代碼中完成的。答:在ps/psspnd.c文件中已經(jīng)為原語Suspend實現(xiàn)了對應的函數(shù)PsSuspendThread: STATUS PsSuspendThread( IN HANDLE hThread )  STATUS

26、 Status; BOOL IntState; PTHREAD Thread; Status = ObRefObjectByHandle(hThread, PspThreadType, (PVOID*)&Thread); if (EOS_SUCCESS(Status)  IntState = KeEnableInterrupts(FALSE);   / 關中斷 if (Ready

27、 = Thread->State)  PspUnreadyThread(Thread); ListInsertTail(&SuspendListHead, &Thread>StateListEntry); Status = STATUS_SUCCESS;  else  Status = STATUS_NOT_SUPPORTED;  KeEnableInterrupts(IntState); 

28、;  / 開中斷 ObDerefObject(Thread);  return Status; 實驗二 進程的同步(7分)1 實驗目的和要求目的:理解進程同步的原理和意義,掌握信號量的實現(xiàn)方法和應用。要求:(1)使用EOS的信號量,實現(xiàn)生產(chǎn)者-消費者問題; (2)跟蹤調試EOS信號量的工作過程,分析EOS信號量實現(xiàn)的源代碼;(3)修改EOS信號量的實現(xiàn)代碼,使之支持等待超時喚醒和批量釋放功能。2 完成的實驗內(nèi)容2.1 使用EOS的信號量實現(xiàn)生產(chǎn)者-消費者問題(簡要說明使用EOS的信號量解決生產(chǎn)者-消費者問題的實現(xiàn)方法;給出在

29、本部分實驗過程中完成的主要工作,包括調試、跟蹤、測試與思考等)生產(chǎn)者和消費者函數(shù)執(zhí)行流程如下圖所示實現(xiàn)方法: 主函數(shù)創(chuàng)建Mutex對象實現(xiàn)信號量的互斥訪問,PV原語的實現(xiàn)過程在Producer函數(shù)和Consumer函數(shù)中給出.同時main函數(shù)創(chuàng)建Empty和Full信號量對象,并創(chuàng)建生產(chǎn)者和消費者進程,當這兩個進程執(zhí)行完后結束main函數(shù).可以看出,main函數(shù)對給出了解決生產(chǎn)者和消費者問題所需要的基本信號量和相應的函數(shù). Producer函數(shù):函數(shù)執(zhí)行時首先檢查是否生產(chǎn)完畢,如果是則結束該函數(shù),否則等待Empty信號量,再檢查Empty是否是空的,是則生產(chǎn).等待Mutex信號量,實現(xiàn)對于臨界

30、資源的互斥訪問,滿足條件時生產(chǎn)一個產(chǎn)品,占用一個緩沖區(qū),并循環(huán)向右移動緩沖區(qū)指針。然后釋放Mutex對象,釋放臨界資源。釋放修改后的Full信號量對象,在此延時500ms在進行循環(huán)生產(chǎn)。 Consumer函數(shù):與Producer函數(shù)一樣,首先檢查是否消費完畢,如果是則結束函數(shù),否則等待Full信號量,如果Full信號量非空,那么Consumer可以消費產(chǎn)品。接著等待Mutex信號量,進入臨界區(qū),實現(xiàn)對于臨界資源的互斥訪問。臨界區(qū)內(nèi)首先執(zhí)行消費一個產(chǎn)品,清空一個緩沖區(qū),循環(huán)向后移動緩沖區(qū)指針,釋放Mutex對象,結束臨界區(qū)訪問,最后釋放Empty信號量對象。函數(shù)最后需要判斷是否是前10個產(chǎn)品,如

31、果是等待2000ms,否則等待100ms,再循環(huán)執(zhí)行。 調試: 使用 pc.c 文件中的源代碼,替換之前創(chuàng)建的 EOS 應用程序項目中 EOSApp.c 文件內(nèi)的源代碼。按 F7 生成修改后的 EOS 應用程序項目。按 F5 啟動調試。OS Lab 會首先彈出一個調試異常對話框。在調試異常對話框中選擇“否”,繼續(xù)執(zhí)行。立即激活虛擬機窗口查看生產(chǎn)者消費者同步執(zhí)行的過程,待應用程序執(zhí)行完畢后,結束此次調試。調試結果如下圖所示思考:生產(chǎn)者線程和消費者線程是如何使用 Mutex、Empty 信號量和 Full 信號量來實現(xiàn)同步的?在兩個線程函數(shù)中對這三個同步對象的操作能夠改變順序嗎?答:三個信號量的初

32、始值分別為Mutex=1(實現(xiàn)臨界資源的互斥訪問)、Empty=10(緩沖區(qū)的容納個數(shù))、Full=0,當存在一個生產(chǎn)者線程訪問緩沖池時,首先對Empty減1,如果大于0,則說明還有剩余緩沖區(qū)可以讓生產(chǎn)者放入產(chǎn)品,否則生產(chǎn)者線程進入等待隊列;再對Mutex減1,如果大于等于0,則說明沒有線程占用緩沖池,否則生產(chǎn)者線程進入等待隊列。生產(chǎn)完產(chǎn)品后,對Mutex加1,解除封鎖;再對Full加1,說明生產(chǎn)了一個產(chǎn)品占用了一個緩沖區(qū)。消費者線程同理,對信號量的操作順序與生產(chǎn)者線程相反。不能對這三個同步對象的操作改變順序,否則可能造成死鎖。生產(chǎn)者在生產(chǎn)了 13 號產(chǎn)品后本來要繼續(xù)生產(chǎn) 14 號產(chǎn)品,可此時

33、生產(chǎn)者為什么必須等待消費者消費了 4 號產(chǎn)品后,才能生產(chǎn) 14 號產(chǎn)品呢?生產(chǎn)者和消費者是怎樣使用同步對象來實現(xiàn)該同步過程的呢?答:因為臨界資源的訪問限制,程序中限定了緩沖池的大小為10,只有緩沖池有空余時生產(chǎn)者才能向里邊放產(chǎn)品,同時只有緩沖池有產(chǎn)品時消費者才能向外取東西。當生產(chǎn)者生產(chǎn)了13號產(chǎn)品后,共生產(chǎn)了從0到13的14個產(chǎn)品,但是只消費了從0到3的4個產(chǎn)品,所以緩沖池中的10個緩沖區(qū)就都被占用了,所以不能繼續(xù)生產(chǎn)14號產(chǎn)品,而要等到消費者消費掉一個產(chǎn)品后,緩沖池有空余位置,才能繼續(xù)生產(chǎn)14號產(chǎn)品。當生產(chǎn)者線程生產(chǎn)了13號產(chǎn)品后,此時Full信號量的值為10,而Empty信號量的值為0,此

34、時若生產(chǎn)者線程要再生產(chǎn)一個產(chǎn)品,先對Empty減1,此時Empty值小于零,生產(chǎn)者線程進入等待隊列;而此時若有一個消費者線程要消費一個產(chǎn)品,先對Full減1,此時Full值為9,大于0,如果沒有線程占用緩沖池,消費者可以消費一個產(chǎn)品。這樣,生產(chǎn)者和消費者就能實現(xiàn)同步過程了。2.2 EOS信號量工作過程的跟蹤與源代碼分析(分析EOS信號量實現(xiàn)的核心源代碼,簡要闡述其實現(xiàn)方法;給出在本部分實驗過程中完成的主要工作,包括調試、跟蹤與思考等)實現(xiàn)方法:EOS信號量實現(xiàn)是按照上面流程圖的操作進行,線面通過分析代碼具體查看執(zhí)行的過程 (1)創(chuàng)建信號量 信號量結構體(SEMAPHORE)中的各個成員變量是由

35、 API 函數(shù) CreateSemaphore 的對應參數(shù)初始化的, Main()函數(shù)的第77行用于創(chuàng)建空信號量EmptySemaphoreHandle = CreateSemaphore(BUFFER_SIZE, BUFFER_SIZE, NULL) CreatSemaphore在流程圖中的三個信號量的創(chuàng)建過程中都有用到,在使用時有必要了解該函數(shù)的執(zhí)行過程CreateSemaphore(IN LONG InitialCount, IN LONG MaximumCount IN PSTR Name) STATUS Status;HANDLE Handle;Status = PsCreateSe

36、maphoreObject(InitialCount,MaximumCount,Name,&Handle );PsSetLastError(TranslateStatusToError(Status);return EOS_SUCCESS(Status) ? Handle : NULL;分析:該函數(shù)使用時參數(shù)包括信號量的初始值、最大值和信號量名稱。PsInitializeSemaphore (IN PSEMAPHORE Semaphore, IN LONG InitialCount, IN LONG MaximumCount)/功能描述:初始化信號量結構體。/參數(shù):Semaphore

37、- 要初始化的信號量結構體指針。InitialCount - 信號量的初始值,不能小于 0 且不能大于 MaximumCount。MaximumCount - 信號量的最大值,必須大于 0。ASSERT(InitialCount >= 0 && InitialCount <= MaximumCount && MaximumCount > 0);Semaphore->Count = InitialCount;Semaphore->MaximumCount = MaximumCount;ListInitializeHead(&S

38、emaphore->WaitListHead);分析:該函數(shù)定義了信號量的結構體,在創(chuàng)建mutex信號量、full信號量和empty信號量的時候,需要預先執(zhí)行該函數(shù)初始化信號量結構體,才能使用其他函數(shù)。 (2)等待、釋放信號量生產(chǎn)者和消費者剛開始執(zhí)行時,用來放產(chǎn)品的緩沖區(qū)都是空的,所以生產(chǎn)者在第一次調用WaitForSingleObject 函數(shù)等待 Empty 信號量時,應該不需要阻塞就可以立即返回。生產(chǎn)者進程代碼:ULONG Producer(PVOID Param) int i;int InIndex = 0;for (i = 0; i < PRODUCT_COUNT; i+

39、) WaitForSingleObject(EmptySemaphoreHandle, INFINITE);WaitForSingleObject(MutexHandle, INFINITE);printf("Produce a %dn", i);BufferInIndex = i;InIndex = (InIndex + 1)% BUFFER_SIZE;ReleaseMutex(MutexHandle); /釋放mutexReleaseSemaphore(FullSemaphoreHandle, 1, NULL);Sleep(500); / 休息一會。每 500 毫秒生產(chǎn)

40、一個數(shù)。return 0;分析:WaitForSingleObject()函數(shù)執(zhí)行了兩次,第一次執(zhí)行是等待empty信號量,第二次執(zhí)行是等待mutex(用于訪問臨界資源定義的信號量),與流程圖中的步驟相對應。BufferInIndex = i;InIndex = (InIndex + 1)% BUFFER_SIZE;則是被訪問的臨界資源,在緩沖區(qū)添加資源,ReleaseMutex函數(shù)釋放mutex信號量,ReleaseSemaphore釋放full信號量。Sleep(500);是定時500ms的函數(shù)。PsWaitForSemaphore函數(shù): PsWaitForSemaphore(IN PSE

41、MAPHORE Semaphore,IN ULONG Milliseconds )/功能描述:信號量的 Wait 操作(P 操作)。/參數(shù):Semaphore - Wait 操作的信號量對象。Milliseconds - 等待超時上限,單位毫秒。返回值:STATUS_SUCCESS。當你修改信號量使之支持超時喚醒功能后,如果等待超時,應該返回 STATUS_TIMEOUT。BOOL IntState;ASSERT(KeGetIntNesting() = 0); / 中斷環(huán)境下不能調用此函數(shù)。IntState = KeEnableInterrupts(FALSE); / 開始原子操作,禁止中斷。

42、/ 目前僅實現(xiàn)了標準記錄型信號量,不支持超時喚醒功能,所以 PspWait 函數(shù)的第二個參數(shù)的值只能是 INFINITE。Semaphore->Count-;if (Semaphore->Count < 0) PspWait(&Semaphore->WaitListHead, INFINITE);KeEnableInterrupts(IntState); / 原子操作完成,恢復中斷。return STATUS_SUCCESS;分析:在Producer進程和Consumer進程函數(shù)中,WaitForSingleObject需要調用此函數(shù),主要是用來完成信號量的等待

43、操作,即P操作。PsReleaseSemaphore函數(shù)STATUSPsReleaseSemaphoreObject(IN HANDLE Handle,IN LONG ReleaseCount,IN PLONGPreviousCount)STATUS Status;PSEMAPHORE Semaphore;if (ReleaseCount < 1) return STATUS_INVALID_PARAMETER; / 由 semaphore 句柄得到 semaphore 對象的指針。Status = ObRefObjectByHandle(Handle, PspSemaphoreType

44、, (PVOID*)&Semaphore);if (EOS_SUCCESS(Status) Status = PsReleaseSemaphore(Semaphore, ReleaseCount, PreviousCount);ObDerefObject(Semaphore);return Status;分析:生產(chǎn)者、消費者和main函數(shù)中都需要使用ReleaseSemaphore來釋放使用過的信號量,函數(shù)有3個句柄,處理子函數(shù),釋放值,當前值。內(nèi)部函數(shù)通過調用PsReleaseSemaphore函數(shù)來具體實現(xiàn)釋放信號量。2.3支持等待超時喚醒和批量釋放功能的EOS信號量實現(xiàn)(給出實現(xiàn)

45、方法的簡要描述、源代碼、測試和結果等)問題:在目前 EOS Kernel 項目的 ps/semaphore.c 文件中,PsWaitForSemaphore 函數(shù)的 Milliseconds 參數(shù)只能是 INFINITE,PsReleaseSemaphore 函數(shù)的 ReleaseCount 參數(shù)只能是 1。現(xiàn)在要求同時修改PsWaitForSemaphore 函數(shù)和 PsReleaseSemaphore 函數(shù)中的代碼,使這兩個參數(shù)能夠真正起到作用,使信號量對象支持等待超時喚醒功能和批量釋放功能。實現(xiàn)方法:修改 PsWaitForSemaphore 函數(shù):對于支持等待超時喚醒功能的信號量,其計

46、數(shù)值只能是大于等于 0。當計數(shù)值大于 0 時,表示信號量為 signaled 狀態(tài);當計數(shù)值等于 0 時,表示信號量為 nonsignaled 狀態(tài)。所以,PsWaitForSemaphore 函數(shù)中原有的代碼段Semaphore->Count-;if (Semaphore->Count < 0) PspWait(&Semaphore->WaitListHead, INFINITE);應被修改為:先用計數(shù)值和 0 比較,當計數(shù)值大于 0 時,將計數(shù)值減 1 后直接返回成功;當計數(shù)值等于 0 時,調用 PspWait 函數(shù)阻塞線程的執(zhí)行(將參數(shù) Milliseco

47、nds 做為 PspWait 函數(shù)的第二個參數(shù),并使用 PspWait 函數(shù)的返回值做為返回值)。修改 PsReleaseSemaphore 函數(shù):使用 ReleaseCount 做為計數(shù)器的循環(huán)體,來替換 PsReleaseSemaphore 函數(shù)中原有的代碼段Semaphore->Count+;if (Semaphore->Count <= 0) PspWakeThread(&Semaphore->WaitListHead, STATUS_SUCCESS);在循環(huán)體中完成下面的工作:1. 如果被阻塞的線程數(shù)量大于等于 ReleaseCount,則循環(huán)結束后,

48、有 ReleaseCount 個線程會被喚醒,而且信號量計數(shù)的值仍然為 0;2. 如果被阻塞的線程數(shù)量(可以為 0)小于 ReleaseCount,則循環(huán)結束后,所有被阻塞的線程都會被喚醒,并且信號量的計數(shù)值ReleaseCount之前被阻塞線程的數(shù)量之前信號量的計數(shù)值。代碼:對PsWaitForSemaphore的修改PsWaitForSemaphore(IN PSEMAPHORE Semaphore, IN ULONG Milliseconds) STATUS value;BOOL IntState;ASSERT(KeGetIntNesting() = 0); / 中斷環(huán)境下不能調用此函數(shù)

49、。IntState = KeEnableInterrupts(FALSE); / 開始原子操作,禁止中斷。/ 目前僅實現(xiàn)了標準記錄型信號量,不支持超時喚醒功能,所以 PspWait 函數(shù)的第二個參數(shù)的值只能是 INFINITE。if (Semaphore->Count > 0) Semaphore->Count-;Status = STATUS_SUCCESS;else Status = PspWait(&Semaphore->WaitListHead, Milliseconds);KeEnableInterrupts(IntState); / 原子操作完成,恢

50、復中斷。return Status; 分析:上面函數(shù)代碼中加黑部分是修改的內(nèi)容,首先判斷信號量的值是否大于0,如果大于零將消耗一個單位的信號量,并且返回消耗成功,否則進行超時等待,并將此等待的狀態(tài)輸出。原代碼僅僅實現(xiàn)了成功時的功能,未進行超時等待。對PsReleaseSemaphore 函數(shù)的修改while(i<ReleaseCount) if(ListIsEmpty(&Semaphore->WaitListHead) break; i+; PspWakeThread(&Semaphore->WaitListHead, STATUS_SUCCESS); if(

51、i>=ReleaseCount) Semaphore->Count=0; if(i<ReleaseCount) Semaphore->Count=ReleaseCount-i+(Semaphore->Count);分析:上述代碼加黑部分是添加的內(nèi)容,主要是喚醒超時等待的進程。當被阻塞的進程的數(shù)量小于釋放數(shù)量時,判斷信號量的等待隊列是否為空,是空的則退出該循環(huán),否則喚醒該進程,并且返回成功狀態(tài)。如果被阻塞的線程數(shù)量(可以為 0)小于 ReleaseCount,則循環(huán)結束后,所有被阻塞的線程都會被喚醒,并且信號量的計數(shù)值ReleaseCount之前被阻塞線程的數(shù)量之前

52、信號量的計數(shù)值。對Producer函數(shù)的修改,添加以下代碼:while(WAIT_TIMEOUT = WaitForSingleObject(EmptySemaphoreHandle, 300) printf("Producer wait for empty semaphore timeoutn"); 分析:該代碼功能是當Empty信號量超時等待的時間超過300ms時,輸出Producer wait for empty semaphore timeout,通過控制臺顯示可以判斷超時等待是否添加成功。對Consumer函數(shù)的修改,添加一下代碼:while(WAIT_TIMEO

53、UT = WaitForSingleObject(FullSemaphoreHandle, 300) printf("Consumer wait for full semaphore timeoutn");分析:添加的代碼是Full信號量的超時等待,時間同樣是300ms,如果等待時間查過300ms,就輸出Consumer wait for full semaphore timeout,表示沒有資源可以消耗測試和結果:添加完代碼后,重新生成debug項目,使用修改完畢的 EOS Kernel 項目生成完全版本的 SDK 文件夾,并覆蓋之前的生產(chǎn)者消費者應用程序項目的 SDK

54、文件夾,選擇調試,運行結果如圖:等待空信號量等待full信號量3 其他需要說明的問題 (1)思考在 ps/semaphore.c 文件內(nèi)的 PsWaitForSemaphore 和PsReleaseSemaphore 函數(shù)中,為什么要使用原子操作? 答:在執(zhí)行PsWaitForSemaphore和PsReleaseSemaphore函數(shù)的時候,不允許cpu響應外部中斷,如果此時cpu響應了外部中斷,會產(chǎn)生不可預料的結果,可能會產(chǎn)生死鎖,無法正常完成函數(shù)的功能。  (2)繪制 ps/semaphore.c 文件內(nèi) PsWaitForSemaphore 和 PsReleaseSemaph

55、ore 函數(shù)的流程圖。 答:實驗三 時間片輪轉調度(5分)1 實驗目的和要求目的:理解進程(線程)調度的執(zhí)行時機和過程,掌握調度程序實現(xiàn)的基本方法。要求:(1)跟蹤調試EOS的線程調度程序,分析EOS基于優(yōu)先級的搶占式調度的源代碼;(2)修改EOS的調度程序,添加時間片輪轉調度。2 完成的實驗內(nèi)容2.1 EOS基于優(yōu)先級的搶占式調度工作過程的跟蹤與源代碼分析(分析EOS基于優(yōu)先級的搶占式調度的核心源代碼,簡要闡述其實現(xiàn)方法;給出在本部分實驗過程中完成的主要工作,包括調試、跟蹤與思考等)本次實驗中的核心源代碼是ke/sysproc.c 文件中第 690 行的ConsoleCmdRoundRobin 函數(shù)及該函數(shù)用到的第 649 行的ThreadFunction 函數(shù)和第 642 行的 THREAD_PARAMETER 結構體,下面是ConsoleCmdRoundRobin的新建的20個優(yōu)先級為8的進程的代碼:/ 新建 20 個優(yōu)先級為 8 的線程。關閉中斷從而保證新建的線程不會

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論