第一篇:讀者-寫者 操作系統實驗報告 計算機操作系統
4.1實驗二:讀者寫者問題 4.1.1實驗要求
在Windows 環境下,創建一個控制臺進程,此進程包含n個線程。用這n個線程來表示n個讀者或寫者。每個線程按相應測試數據文件(后面有介紹)的要求進行讀寫操作。用信號量機制分別實現讀者優先和寫者優先的讀者-寫者問題。
讀者-寫者問題的讀寫操作限制(包括讀者優先和寫者優先): 1)寫-寫互斥,即不能有兩個寫者同時進行寫操作。
2)讀-寫互斥,即不能同時有一個線程在讀,而另一個線程在寫。3)讀-讀允許,即可以有一個或多個讀者在讀。
讀者優先的附加限制:如果一個讀者申請進行讀操作時已有另一個讀者正在進行讀操作,則該讀者可直接開始讀操作。
寫者優先的附加限制:如果一個讀者申請進行讀操作時已有另一寫者在等待訪問共享資源,則該讀者必須等到沒有寫者處于等待狀態才能開始讀操作。
運行結果顯示要求:要求在每個線程創建、發出讀寫操作申請、開始讀寫操作和結果讀寫操作時分別顯示一行提示信息,以確定所有處理都遵守相應的讀寫操作限制。4.1.2測試數據文件格式
測試數據文件包括n行測試數據,分別描述創建的n個線程是讀者還是寫者,以及讀寫操作的開始時間和持續時間。每行測試數據包括四個字段,各個字段間用空格分隔。第一字段為一個正整數,表示線程序號。第二字段表示相應線程角色,R表示讀者,W表示寫者。第三字段為一個正數,表示讀寫操作的開始時間:線程創建后,延遲相應時間(單位為秒)后發出對共享資源的讀寫申請。第四字段為一個正數,表示讀寫操作的持續時間。當線程讀寫申請成功后,開始對共享資源的讀寫操作,該操作持續相應時間后結束,并釋放共享資源。
下面是一個測試數據文件的例子: 1 R 3 5 2 W 4 5 3 R 5 2 4 R 6 5 5 W 5.1 3 注意:
在創建數據文件時,由于涉及到文件格式問題,最好在記事本中手工逐個鍵入數據,而不要拷貝粘貼數據,否則,本示例程序運行時可能會出現不可預知的錯誤。4.1.3實習分析
可以將所有讀者和所有寫者分別存于一個讀者等待隊列和一個寫者等待隊列中,每當讀允許時,就從讀者隊列中釋放一個或多個讀者線程進行讀操作;每當寫允許時,就從寫者隊列中釋放一個寫者進行寫操作。
1.讀者優先
讀者優先指的是除非有寫者在寫文件,否則讀者不需要等待。所以可以用一個整型變量read-count記錄當前的讀者數目,用于確定 是否需要釋放正在等待的寫者線程(當read-count=0時,表明所有的讀者讀完,需要釋放寫者等待隊列中的一個寫者)。每一個讀者開始讀文件時,必須修改read-count變量。因此需要一個互斥對象mutex來實現對全局變量read-count修改時的互斥。
另外,為了實現寫-寫互斥,需要增加一個臨界區對象write。當寫者發出寫請求時,必須申請臨界區對象的所有權。通過這種方法,也可以實現讀-寫互斥,當read-count=1時(即第一個讀者到來時),讀者線程也必須申請臨界區對象的所有權。當讀者擁有臨界區的所有權時,寫者阻塞在臨界區對象write上。當寫者擁有臨界區的所有權時,第一個讀者判斷完“read-count==1”后阻塞在write上,其余的讀者由于等待對read-count的判斷,阻塞在mutex上。
2.寫者優先
寫者優先與讀者優先類似。不同之處在于一旦一個寫者到來,它應該盡快對文件進行寫操作,如果有一個寫者在等待,則新到來的讀者不允許進行讀操作。為此應當添加一個整型變量write-count,用于記錄正在等待的寫者數目,當write-count=0時,才可以釋放等待的讀者線程隊列。
為了對全局變量write-count實現互斥,必須增加一個互斥對象mutex3。
為了實現寫者優先,應當添加一個臨界區對象read,當有寫者在寫文件或等待時,讀者必須阻塞在read上。
讀者線程除了要對全局變量read-count實現操作上的互斥外,還必須有一個互斥對象對阻塞read這一過程實現互斥。這兩個互斥對象分別命名為mutex1和mutex2。4.1.4相關API函數說明
1.CreateThread 函數功能:
該函數創建一個在調用進程的地址空間中執行的線程。函數原型:
HANDLE CreateThread(LPSECURITY-ATTRIBUTES lpThreadAttributes, DWORD dwStackSize,LPTHREAD-START-TOUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LLPDWORD lpThreadId);參數:
·lpThreadAttributes:指向一個SECURITY-ATTRIBUTES結構,該結構決定了返回的句柄是否可被子進程繼承。若lpThreadAttributes為NULL,則句柄不能被繼承。在Windows NT中該結構的lpSwcurityDescriptor成員定義了新進程的安全性描述符。若lpThreadAttributes為NULL,則線程獲得一個默認的安全性描述符。·dwStackSize:定義原始堆棧提交時的大小(按字節計)。系統將該值舍入為最近的頁。若該值為0,或小于默認時提交的大小,默認情況是使用與調用線程同樣的大小。更多的信息,請看Thread Stack Size。
·lpStartAddress:指向一個LPTHREAD-START-TOUTINE類型的應用定義的函數,該線程執行此函數。該指針還表示遠程進程中線程的起始地址。該函數必須存在于遠程進程中。
·lpParameter:定義一個傳遞給該進程的32位值。
·dwCreationFlags:定義控制進程創建的附加標志。若定義CREATE-SUSPENDED標志,線程創建時處于掛起狀態,并且直到ResumeThread函數調用時才能運行。若該值為0,則該線程在創建后立即執行。
·lpThreadId:指向一個32位值,它接收該線程的標識符。返回值:
若函數調用成功,返回值為新線程的句柄;若函數調用失敗,返回值為NULL。備注:
新線程的句柄創建時設為THREAD-ALL-ACCESS訪問權限。若未提供安全性描述符,則該句柄可被任何要求一個線程對象句柄的函數所使用。若提供了安全性描述符,則以后使用該句柄時,將在授權訪問以前執行訪問檢查。若訪問檢查被拒絕訪問,則請求進程不能使用該句柄獲得對該線程的訪問。線程從lpStartAddress參數定義的函數處開始執行。若該函數返回,系統將默認地認為以調用ExitThread函數的方法終止該線程。使用GetExitCodeThread 函數來獲得線程的返回值。
線程創建時擁有THREAD-PRIORITY-NORMAL優先權。使用GetThreadPriority和SetThreadPriority函數可以獲得和設置線程的優先權值。
一個線程終止時,該線程對象被設為發信號狀態,以滿足在該對象上等待的所有進程。一個線程對象始終存在于系統中,直到該線程終止,且它所有的句柄都已通過調用CloseHandle函數關閉。
2.ExitThread 函數功能:
該函數結束一個線程。函數原型:
VOID ExitThread(DWORD dwExitcode); 參數:
·dwExitcode:定義調用線程的退出代碼。使用GetExitcodeThread函數來檢測一個線程的退出代碼。返回值:無。備注:
調用ExitThread函數,是結束一個線程的較好的方法。調用該函數后(或者直接地調用,或者從一個線程過程返回),當前線程的堆棧取消分配,線程終止。若調用該函數時,該線程為進程的最后一個線程,則該線程的進程也被終止。
線程對象的狀態變為發信號狀態,以釋放所有正在等待該線程終止的其他線程。線程的終止狀態從STILL-ACTIVATE變為dwExitcode參數的值。
線程結合時不必從操作系統中移去該線程對象。當線程的最后一個句柄關閉時,該線程對象被刪除。
3.SLEEP 函數功能:
該函數對于指定的時間間隔掛起當前的執行線程。函數原型:
VOID SLEEP(DWORD dwMilliseconds); 參數:
·dwMilliseconds:定義掛起執行線程的時間,以毫秒(ms)為單位。取值為0時,該線程將如余下的時間片交給處于就緒狀態的同一優先級的其他線程。若沒有處于就緒狀態的同一優先級的其他線程,則函數立即返回,該線程繼續執行。若取值為INFINITE則造成無限延遲。返回值:
該函數沒有返回值。備注:
一個線程可以在調用該函數時將睡眠時間設為0ms,以將剩余的時間片交出。4.CreateMutex 函數功能:
該函數創建有名或者無名的互斥對象。函數原型:
HANDLE CreateMutex(LPSECURITY-ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner,LPCTSTR lpName);參數:
·lpMutexAttributes:指向SECURITY-ATTRIBUTES結構的指針,該結構決定子進程是否能繼承返回句柄。如果lpMutexAttributes為NULL,那么句柄不能被繼承。
在Windows NT中該結構的lpSwcurityDescriptor成員指定新互斥對象的安全性描述符。若lpThreadAttributes為NULL,那么互斥對象獲得默認的安全性描述符。·bInitialOwner:指定互斥對象的初始所屬身份。如果該值為TRUE,并且調用者創建互斥對象,那么調用線程獲得互斥對象所屬身份。否則,調用線程不能獲得互斥對象所屬身份。判斷調用者是否創建互斥對象請參閱返回值部分。
·lpName:指向以NULL結尾的字符串,該字符串指定了互斥對象名。該名字的長度大于MAX-PATH且可以包含除反斜線()路徑分隔符以外的任何字符。名字是區分大小寫的。
如果lpName與已存在的有名互斥對象相匹配,那么該函數要求用MUTEX-ALL-ACCESS權限訪問已存在的對象。在這種情況下,由于參數bInitialOwner已被創建進程所設置,該參數被忽略。如果參數lpMutexAttributes不為NULL,它決定句柄是否解除繼承,但是其安全描述符成員被忽略。
如果lpName為NULL,那么創建的互斥對象無名。
如果lpName與已存在的事件、信號量、可等待定時器、作業或者文件映射對象的名字相匹配,那么函數調用失敗,并且GetLastError函數返回ERROR-INVALID-HANDLE,其原因是這些對象共享相同的名字空間。
返回值:
如果函數調用成功,返回值為互斥對象句柄;如果函數調用之前,有名互斥對象已存在,那么函數給已存在的對象返回一個句柄,并且函數GetLastError返回ERROR-ALREADY-EXISTS,否則,調用者創建互斥對象。
如果函數調用失敗,則返回值為NULL。若想獲得更多錯誤信息,請調用GetLastError函數。
備注:
由函數CreateMutex返回的句柄有MUTEX-ALL-ACCESS權限可以去訪問新的互斥對象,并且可用在請求互斥對象句柄的任何函數中。
調用進程中的任何線程可以可以在調用等待函數時指定互斥對象句柄。當指定對象的狀態為信號態時,返回單對象等待函數。當任何一個或者所有的互斥對象都為信號態時,返回多對象等待函數指令。等待函數返回后,等待的線程被釋放,繼續向下執行。
當一個互斥對象不被任何線程擁有時,處于信號態。創建該對象的線程可以使用bInitialOwner標志來請求立即獲得對該互斥對象的所有權。否則,線程必須使用等待函數來請求所有權。當互斥對象處于信號態,等待的線程獲得對該對象的所有權時,此互斥對象的狀態被設置為非信號態,等待函數返回。任意時刻,僅有一個線程能擁有該互斥對象,線程可以使用ReleaseMutex函數來釋放對這個互斥對象的所有權。
若線程已經擁有了一個互斥對象,那么它可以重復調用等待函數而不會發生阻塞,一般情況下,用戶不會重復等待同一個互斥對象,這種機制防止了線程因等待它已經擁有的互斥對象而發生死鎖。然而,線程必須為每一次等待調用一次ReleaseMutex函數來釋放該互斥對象。
兩個或多個互斥進程可以調用CreateMutex來創建同名的互斥對象,第一個進程實際創建互斥對象,以后的進程打開已存在的互斥對象的句柄。這使得多個進程可以得到同一個互斥對象的句柄,從而減輕了用戶的負擔,使用戶不必判斷創建進程是否為第一個啟動的進程。使用這種技術時,應該把bInitialOwner標志設為FALSE;否則很難確定開始時哪一個進程擁有該互斥對象。
由于多進程能夠擁有相同互斥對象的句柄,通過使用這個對象,可使多進程同步。以下為共享對象機制:
·如果CreateMutex中的lpMutexAttributes參數允許繼承,由CreateProcess函數創建的子進程可以繼承父進程的互斥對象句柄。
·一個進程可以在調用DuplicateHandle函數時指定互斥對象句柄來創建一個可以被其他進程使用的雙重句柄。一個進程在調用OpenMutex或CreateMutex函數時能指定互斥對象名。
·使用CloseHandle函數關閉句柄,進程時系統自動關閉句柄。當最后一個句柄被關閉時,互斥對象被銷毀。
5.ReleaseMutex 函數功能:
該函數放棄指定互斥對象的所有權。函數原型:
BOOL ReleaseMutex(HANDLE hMutex); 參數:
·hMutex:互斥對象句柄。為CreateMutex或OpenMutex函數的返回值。返回值:
如果函數調用成功,那么返回值是非零值;如果函數調用失敗,那么返回值是零值。若想獲得更多錯誤信息,請調用GetLastError函數。
備注:
如果調用線程不擁有互斥對象,ReleaseMutex函數失敗。一個線程通過調用等待函數擁有互斥對象。創建該互斥對象的線程也擁有互斥對象,而不需要調用等待函數。當互斥對象的所有者線程不再需要互斥對象時,它可以調用ReleaseMutex函數。
當一個線程擁有一個互斥對象后,它可以用該互斥對象多次調用等待函數而不會阻塞。這防止一個線程等待一個它擁有的互斥對象時出現死鎖。不過,為了釋放所有權,該線程必須為每一個等待操作調用一次ReleaseMutex函數。
6.WaitForSingleObject 函數功能:
當下列情況之一發生時該函數返回:(1)指定對象處于信號態;(2)超時。函數原型:
DWORD waitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);參數:
·hHandle:等待對象句柄。若想了解指定句柄的對象類型列表,參閱下面備注部分。
在Windows NT中,句柄必須有SYNCHRONIZE訪問權限。若想獲得更多的信息,請查看Standard Access Rights。
·dwMilliseconds:指定以毫秒為單位的超時間隔。如果超時,即使對象的狀態是非信號態的并且沒有完成,函數也返回。如果dwMilliseconds是0,函數測試對象的狀態并立即返回;如果dwMilliseconds是INFINITE,函數從不超時。返回值:
如果函數調用成功,返回值表明引起函數返回的事件。可能值如下:
·WAIT-ABANDONED:指定對象是互斥對象,在線程被終止前,線程沒有釋放互斥對象。互斥對象的所屬關系被授予調用線程,并且該互斥對象被置為非信號態。·WAIT-OBJECT-0:指定對象的狀態被置為信號態。
·WAIT-TIMEOUT:超時,并且對象的狀態為非信號態。
如果函數調用失敗,返回值是WAIT-FAILED。若想獲得更多錯誤信息,請調用GetLastError函數。
備注:
waitForSingleObjects函數決定等待條件是否被滿足。如果等待條件并沒有被滿足,調用線程進入一個高效的等待狀態,當等待滿足條件時占用非常少的處理機時間。
在運行前,一個等待函數修改同步對象類型的狀態。修改僅發生在引起函數返回的對象身上。例如,信號的計數減1。
WaitForSingleObjects函數能等待的對象包括:Change notification(改變通告);Consoleinput(控制臺輸入);Event(事件);Job(作業);Mutex(互斥對象);Process(進程);Semaphore(信號量);Thread(線程);Waitable timer(可等待定時器)。
當使用等待函數或代碼直接或間接創建窗口時,一定要小心。如果一個線程創建了任何窗口,它必須處理進程消息。消息廣播被發送到系統的所有窗口。一個線程用沒有超時的等待函數也許會引起系統死鎖。間接創建窗口的兩個例子是DDE和COM CoInitialize。因此,如果用戶有一個創建窗口的線程,用MsgWaitForMultipleObjects或MsgWaitForMultipleObjectsEx函數,而不要用SignalObjectAndWait函數。
7.WaitForMultipleObjects 函數功能:
WaitForMultipleObjects函數當下列條件之一滿足時返回:(1)任意一個或全部指定對象處于信號態;(2)超時間隔以過。
函數原型:
DWORD WaitForMultipleObjects(DWORD nCount,CONST HANDLE *lpHandles, BOOL fWaitAll,DWORD dwMilliseconds);參數:
·nCount:指定由lpHandles所指向的數組中的句柄對象數目MAXIMUM-WAIT-OBJECTS。
·lpHandles:指向對象句柄數組的指針。該數組可以包含不同類型對象的句柄。在Windows NT中,句柄必須有SYNCHRONIZE訪問權限。若想獲得更多的信息,請查看Standard Access Rights。
·fWaitall:指定等待類型。如果為TRUE,當lpHandles指向的數組里的全部對象為信號態時,函數返回。如果為FALSE,當由lpHandles指向的數組里的任一對象為信號態時,函數返回。對于后者,返回值指出引起函數返回的對象。
·dwMilliseconds:指定以毫秒為單位的超時間隔。如果超時,即使bWaitAll參數指定的條件沒有滿足,函數也返回。如果dwMilliseconds是0,函數測試對象的狀態并立即返回;如果dwMilliseconds是INFINITE,函數從不超時。
返回值:
如果函數調用成功,返回值表明引起函數返回的事件。可能值如下:
·WAIT-OBJECT-0到WAIT-OBJECT-0+nCount-1:如果bWaitAll為TRUE,那么返回值表明所有指定對象的狀態為信號態。如果bWaitAll為FALSE,那么返回值減去WAIT-OBJECT-0表明引起函數返回的對象的pHandles數組索引。如果多于一個對象變為信號態,則返回的是數組索引最小的信號態對象索引。
·WAIT-ABANDONED-0到·WAIT-ABANDONED-0+ nCount-1:如果bWaitAll為TRUE,那么返回值表明所有指定對象的狀態為信號態,并且至少一個對象是已放棄的互斥對象。如果bWaitAll為FALSE,那么返回值減去WAIT-OBJECT-0表明引起函數返回的放棄互斥對象的pHandles數組索引。
·WAIT-TIMEOUT:超時并且由參數bWaitAll指定的條件沒有滿足。
如果函數調用失敗,返回值是WAIT-FAILED。若想獲得更多錯誤信息,請調用GetLastError函數。
8.CreateSemapore 函數功能:
該函數是創建一個有名或者無名信號對象。函數原型:
HANDLE CreateSwmaphore(LPSECURITY-ATTRIBUTES lpAttributes,LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName);參數:
·lpAttributes:安全屬性。如果是NULL就表示要使用默認屬性。·lInitialCount:Semapore的初值。必須大于或等于0,并且小于或等于MaximumCount。·lMaximumCount:Semapore的最大值。這也就是在同一時間內能夠鎖住Semapore之線程的最多個數。
·lpName:Semapore的名稱(一個字符串)。任何線程(或進程)都可以根據這一名稱引用到這個Semaphore。這個值可以是NULL,意思是產生一個沒有名字的Semaphore。
返回值:
如果成功就傳回一個handle,否則傳回NULL。不論哪一種情況,GetLastError都會傳回一個合理的結果。如果指定的Semaphore名稱已經存在,則該函數還是成功的,GetLastError會傳回ERROR_ALREADY_EXISTS。
9.ReleaseSemaphore 函數功能:
該函數將指定信號對象的計數增加一個指定的數量。函數原型:
BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount,LPLONG lpPreviousCount);參數:
·hSemaphore:Semaphore的handle。
·lReleaseCount:Semaphore現值的增額。該值不可以是負值或0。·lpPreviousCount:借此返回Semaphore原來的值。返回值:
如果成功,則返回TRUE。否則返回FALSE。失敗時可調用GetLastError獲得原因。備注:
無論ReleaseSemaphore對于Semaphore所造成的當前值怎樣增加,都絕對不會超過CreateSemaphore時所指定的ImaximumCount。
請記住,lpPreviousCount所傳回來的是一個瞬間值。你不可以把lReleaseCount加上* lpPreviousCount,就當做是Semaphore的當前值,因為其他線程可能已經改變了Semaphore的值。
與mutex不同的是,調用ReleaseSemaphore的那個線程,并不一定就是調用WaitXxx 的那個線程。任何線程都可以在任何時候調用ReleaseSemaphore,解除被任何線程鎖定的Semaphore。10.InitializeCriticalSection 函數功能:
該函數初始化臨界區對象。函數原型:
VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 參數:
·lpCriticalSection:指向臨界區對象的指針。備注:
單進程的所有線程可以使用互斥同步機制的臨界區對象。但是,不能保證線程獲得臨界區所有權的順序,系統將對所有線程公平處理。
進程負責分配臨界區對象使用的存儲空間,這可以通過聲明CRITICAL_SECTION類型的變量來完成。在使用臨界區之前,該進程的一些線程必須使用InitializeCriticalSection或InitializeCriticalSectionAndSectiom函數來初始化該臨界區對象。
11.EnterCriticalSection 函數功能:該函數是等待指定臨界區對象的所有權。當調用線程被賦予所有權時,該函數返回。
函數原型:
VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 參數:
·lpCriticalSection:指向臨界區對象的指針。12.LeaveCriticalSection 函數功能:該函數釋放指定臨界區對象的所有權。函數原型:
VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection); 參數:
·lpCriticalSection:指向臨界區對象的指針。4.1.5參考源代碼
下面的程序已經在Windows 2000/XP上實現。用VC6.0創建源文件,將輸入文件命名為thread.dat并放在與源文件相同的文件夾內,編譯運行即可(本節中的參考源代碼僅供參考)。
#include “windows.h” #include
#define READER 'R'
// 讀者 #define WRITER 'W'
// 寫者
#define INTE_PER_SEC 1000
// 每秒時鐘中斷數目。#define MAX_THREAD_NUM 64
// 最大線程數目 #define MAX_FILE_NUM 32
// 最大數據文件數目 #define MAX_STR_LEN 32
// 字符串長度
int readcount=0;
// 讀者數目 int writecount=0;
// 寫者數目
CRITICAL_SECTION RP_Write;
//臨界區 CRITICAL_SECTION cs_Write;CRITICAL_SECTION cs_Read;struct ThreadInfo { int
serial;
// 線程序號
char entity;
//線程類別(判斷讀者線程還是寫者線程)double delay;double persist;};
/////////////////////////////////////////////////////////////////////////// // 讀者優先--讀者線程 //p:讀者線程信息
void RP_ReaderThread(void* p){
//互斥變量
HANDLE h_Mutex;h_Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,“mutex_for_readcount”);
DWORD wait_for_mutex;
//等待互斥變量所有權 DWORD m_delay;
// 延遲時間
DWORD m_persist;
// 讀文件持續時間 int m_serial;
//線程序號 //從參數中獲得信息
m_serial=((ThreadInfo*)(p))->serial;m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);m_persist=(DWORD)(((ThreadInfo*)(p))->persist*INTE_PER_SEC);Sleep(m_delay);
//延遲等待
printf(“Reader thread %d sents the reading require.n”,m_serial);
// 等待互斥信號,保證對readcount的訪問、修改互斥 wait_for_mutex=WaitForSingleObject(h_Mutex,-1);//讀者數目增加 readcount++;if(readcount==1){
//第一個讀者,等待資源
EnterCriticalSection(&RP_Write);} ReleaseMutex(h_Mutex);
//釋放互斥信號
//讀文件
printf(“Reader thread %d begins to read file.n”,m_serial);Sleep(m_persist);
// 退出線程
printf(“Reader thread %d finished reading file.n”,m_serial);//等待互斥信號,保證對readcount的訪問、修改互斥 wait_for_mutex=WaitForSingleObject(h_Mutex,-1);//讀者數目減少 readcount--;if(readcount==0){
//如果所有讀者讀完,喚醒寫者
LeaveCriticalSection(&RP_Write);} ReleaseMutex(h_Mutex);
//釋放互斥信號 }
/////////////////////////////////////////////////////////////////////////// // 讀者優先--寫者線程 //p:寫者線程信息
void RP_WriterThread(void* p){ DWORD m_delay;
// 延遲時間
DWORD m_persist;
// 寫文件持續時間 int m_serial;
//線程序號 //從參數中獲得信息
m_serial=((ThreadInfo*)(p))->serial;m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);m_persist=(DWORD)(((ThreadInfo*)(p))->persist*INTE_PER_SEC);Sleep(m_delay);
//延遲等待
printf(“Writer thread %d sents the writing require.n”,m_serial);// 等待資源
EnterCriticalSection(&RP_Write);
//寫文件 printf(“Writer thread %d begins to Write to the file.n”,m_serial);Sleep(m_persist);
// 退出線程
printf(“Writer thread %d finished Writing to the file.n”,m_serial);//釋放資源
LeaveCriticalSection(&RP_Write);}
/////////////////////////////////////////////////////////////////////////// // 讀者優先處理函數 //file:文件名
void ReaderPriority(char* file){ DWORD n_thread=0;
//線程數目 DWORD thread_ID;
//線程ID DWORD wait_for_all;
//等待所有線程結束 //互斥對象
HANDLE h_Mutex;h_Mutex=CreateMutex(NULL,FALSE,“mutex_for_readcount”);
//線程對象的數組
HANDLE h_Thread[MAX_THREAD_NUM];ThreadInfo thread_info[MAX_THREAD_NUM];
readcount=0;
// 初始化 readcount
InitializeCriticalSection(&RP_Write);
//初始化臨界區 ifstream inFile;inFile.open(file);
//打開文件 printf(“Reader Priority:nn”);while(inFile){
//讀入每一個讀者、寫者的信息
inFile>>thread_info[n_thread].serial;
inFile>>thread_info[n_thread].entity;
inFile>>thread_info[n_thread].delay;
inFile>>thread_info[n_thread++].persist;
inFile.get();} for(int i=0;i<(int)(n_thread);i++){
if(thread_info[i].entity==READER || thread_info[i].entity=='R')
{
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RP_ReaderThread),&thread_info[i],0,&thread_ID);//創建讀者線程
} else{
//創建寫者線程
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RP_WriterThread),&thread_info[i],0,&thread_ID);
} }
//等待所有線程結束
wait_for_all=WaitForMultipleObjects(n_thread,h_Thread,TRUE,-1);printf(“All reader and writer have finished operating.n”);}
/////////////////////////////////////////////////////////////////////////// // 寫者優先--讀者線程 //p:讀者線程信息
void WP_ReaderThread(void* p){
//互斥變量
HANDLE h_Mutex1;h_Mutex1=OpenMutex(MUTEX_ALL_ACCESS,FALSE,“mutex1”);HANDLE h_Mutex2;h_Mutex2=OpenMutex(MUTEX_ALL_ACCESS,FALSE,“mutex2”);
DWORD wait_for_mutex1;
//等待互斥變量所有權 DWORD wait_for_mutex2;DWORD m_delay;
// 延遲時間
DWORD m_persist;
// 讀文件持續時間 int m_serial;
//線程序號 //從參數中獲得信息
m_serial=((ThreadInfo*)(p))->serial;m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);m_persist=(DWORD)(((ThreadInfo*)(p))->persist*INTE_PER_SEC);Sleep(m_delay);
//延遲等待
printf(“Reader thread %d sents the reading require.n”,m_serial);wait_for_mutex1= WaitForSingleObject(h_Mutex1,-1);//進入讀者臨界區
EnterCriticalSection(&cs_Read);// 阻塞互斥對象mutex2,保證對readcount的訪問、修改互斥 wait_for_mutex2= WaitForSingleObject(h_Mutex2,-1);//修改讀者數目 readcount++;if(readcount==1){
//如果是第一個讀者,等待寫者寫完
EnterCriticalSection(&cs_Write);} ReleaseMutex(h_Mutex2);
//釋放互斥信號mutex2 // 讓其他讀者進入臨界區
LeaveCriticalSection(&cs_Write);ReleaseMutex(h_Mutex1);//讀文件
printf(“Reader thread %d begins to read file.n”,m_serial);Sleep(m_persist);// 退出線程
printf(“Reader thread %d finished reading file.n”,m_serial);// 阻塞互斥對象mutex2,保證對readcount的訪問、修改互斥 wait_for_mutex2= WaitForSingleObject(h_Mutex2,-1);readcount--;if(readcount==0){
// 最后一個讀者,喚醒寫者
LeaveCriticalSection(&cs_Write);} ReleaseMutex(h_Mutex2);
//釋放互斥信號 } /////////////////////////////////////////////////////////////////////////// // 寫者優先--寫者線程 //p:寫者線程信息
void WP_WriterThread(void* p){ DWORD m_delay;
// 延遲時間
DWORD m_persist;
// 寫文件持續時間 int m_serial;
//線程序號 DWORD wait_for_mutex3;//互斥對象
HANDLE h_Mutex3;h_Mutex3= OpenMutex(MUTEX_ALL_ACCESS,FALSE,“mutex3”);
//從參數中獲得信息
m_serial=((ThreadInfo*)(p))->serial;m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);m_persist=(DWORD)(((ThreadInfo*)(p))->persist*INTE_PER_SEC);Sleep(m_delay);
//延遲等待 printf(“Writer thread %d sents the writing require.n”,m_serial);
// 阻塞互斥對象mutex3,保證對writecount的訪問、修改互斥 wait_for_mutex3= WaitForSingleObject(h_Mutex3,-1);writecount++;
//修改讀者數目 if(writecount==1){
//第一個寫者,等待讀者讀完
EnterCriticalSection(&cs_Read);} ReleaseMutex(h_Mutex3);
//進入寫者臨界區
EnterCriticalSection(&cs_Write);//寫文件
printf(“Writer thread %d begins to Write to the file.n”,m_serial);Sleep(m_persist);
// 退出線程
printf(“Writer thread %d finishing Writing to the file.n”,m_serial);
//離開臨界區
LeaveCriticalSection(&cs_Write);
// 阻塞互斥對象mutex3,保證對writecount的訪問、修改互斥 wait_for_mutex3= WaitForSingleObject(h_Mutex3,-1);writecount--;
//修改讀者數目 if(writecount==0){
//寫者寫完,讀者可以讀
LeaveCriticalSection(&cs_Read);} ReleaseMutex(h_Mutex3);}
/////////////////////////////////////////////////////////////////////////// // 寫者優先處理函數 //file:文件名
void WriterPriority(char* file){ DWORD n_thread=0;
//線程數目 DWORD thread_ID;
//線程ID DWORD wait_for_all;
//等待所有線程結束
//互斥對象
HANDLE h_Mutex1;h_Mutex1=CreateMutex(NULL,FALSE,“mutex1”);HANDLE h_Mutex2;h_Mutex2=CreateMutex(NULL,FALSE,“mutex2”);HANDLE h_Mutex3;h_Mutex3=CreateMutex(NULL,FALSE,“mutex3”);
//線程對象
HANDLE h_Thread[MAX_THREAD_NUM];ThreadInfo thread_info[MAX_THREAD_NUM];
readcount=0;
// 初始化 readcount writecount=0;
// 初始化writecount InitializeCriticalSection(&cs_Write);
//初始化臨界區 InitializeCriticalSection(&cs_Read);ifstream inFile;inFile.open(file);
//打開文件 printf(“Writer Priority:nn”);while(inFile){
//讀入每一個讀者、寫者的信息
inFile>>thread_info[n_thread].serial;
inFile>>thread_info[n_thread].entity;
inFile>>thread_info[n_thread].delay;
inFile>>thread_info[n_thread++].persist;
inFile.get();} for(int i=0;i<(int)(n_thread);i++){
if(thread_info[i].entity==READER || thread_info[i].entity=='R')
{
//創建讀者線程
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RP_WriterThread),&thread_info[i],0,&thread_ID);
} else{
//創建寫者線程
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(WP_WriterThread),&thread_info[i],0,&thread_ID);
} }
//等待所有線程結束
wait_for_all=WaitForMultipleObjects(n_thread,h_Thread,TRUE,-1);printf(“All reader and writer have finished operating.n”);} /////////////////////////////////////////////////////////////////////////////// //主函數
int main(int argc,char* argv[]){ char ch;
while(true){
//打印提示信息
printf(“************************************************n”);
printf(“
1:Reader Priorityn”);
printf(“
2:Writer Priorityn”);
printf(“
3:Exit Priorityn”);
printf(“************************************************n”);
printf(“Enter your choice(1,2 or 3): ”);
//如果輸入信息不正確,繼續輸入
do{
ch=(char)_getch();
}while(ch!= '1' &&ch!= '2' && ch!= '3');
system(“cls”);
//選擇3,返回
if(ch=='3')
return 0;
//選擇1,讀者優先
else if(ch=='1')
ReaderPriority(“thread.dat”);
//選擇2,寫者優先
else if(ch=='2')
WriterPriority(“thread.dat”);
//結束
printf(“nPress Any Key To Continue: ”);
_getch();
system(“cls”);} return 0;}
說明:
在Win32 API中,互斥對象Mutex與P、V中的互斥信號量有類似的地方,但也有不同:在P、V操作中的互斥信號量可以有一個任意大小的初值,但互斥對象Mutex沒有,它可以被看成是初值為1的互斥信號量。而且一個線程在取得Mutex的所有權之后,即使不調用ReleaseMutex函數,在線程結束時,線程也會自動釋放Mutex的所有權。
臨界區對象CriticalSection則與P、V操作中初值為1的互斥信號量語意相同。它在線程結束時會將CriticalSection的所有權傳遞給它的同類型線程。這樣就可以滿足在一個線程中申請所有權,在另一個線程釋放所有權的要求。在讀者優先中的write互斥信號量以及寫者優先中的read和write互斥信號量就應該用CriticalSection實現而不應該用Mutex。
用WaitForSingleSignal函數可以獲得一個Mutex的所有權,類似于P操作,而ReleaseMutex函數可以釋放一個Mutex的所有權,類似于V操作。
用EnterCriticalSection函數可以進入一個CriticalSection,類似于P操作,而LeaveCriticalSection函數離開一個CriticalSection,類似于V操作。
備注:
ReaderPriority和WritePriority函數最后都有
wait_for_all=WaitForMultipleObjects(n_thread,h_Thread,TRUE,-1);是因為主函數要等待所有的線程都結束之后才退出。因為不知道有多少線程,所以源文件最初有:
#define MAX_THREAD_NUM 64 //最大線程數目
即線程最多不能超過MAX_THREAD_NUM個。線程對象的數組大小為MAX_THREAD_NUM。如果創建的線程沒有那么多,空間會有浪費,但是可以達到犧牲空間來節省時間的目的。
有的書上還有其他的處理方法。一種是在主函數的最后加上Sleep(1000),即通過主函數睡眠的方法等待其他線程結束,這當然不是一種很好的方法,因為睡眠等待的時間沒法控制。另一種方法是增加循環變量threadCount(線程的個數),每個線程結束的就會執行語句
threadCount--;主函數的最后測試:
while(threadcount>0);但是這種方式會讓主函數循環等待,浪費了CPU資源。
相比之下,考慮到運行效率,還是實例中給出的方法比較好寫些。
第二篇:操作系統讀者寫者實驗報告
目錄
一 設計概述 ……………………………………………………2
二 設計目的與內容 ……………………………………………3
三 設計分析 ……………………………………………………4
四 程序實現 ……………………………………………………5
五 程序調試 ……………………………………………………6
六 結果分析和討論 ……………………………………………6
七 心得體會 ……………………………………………………7
八 源代碼 ………………………………………………………7
一 設計概述
所謂讀者寫者問題,是指保證一個writer進程必須與其他進程互斥地訪問共享對象的同步問題。
讀者寫者問題可以這樣的描述,有一群寫者和一群讀者,寫者在寫同一本書,讀者也在讀這本書,多個讀者可以同時讀這本書,但是,只能有一個寫者在寫書,并且,讀者必寫者優先,也就是說,讀者和寫者同時提出請求時,讀者優先。當讀者提出請求時需要有一個互斥操作,另外,需要有一個信號量S來當前是否可操作。
信號量機制是支持多道程序的并發操作系統設計中解決資源共享時進程間的同步與互斥的重要機制,而讀者寫者問題則是這一機制的一個經典范例。
與記錄型信號量解決讀者—寫者問題不同,信號量機制它增加了一個限制,即最多允許RN個讀者同時讀。為此,又引入了一個信號量L,并賦予初值為RN,通過執行wait(L,1,1)操作,來控制讀者的數目,每當有一個讀者進入時,就要執行wait(L,1,1)操作,使L的值減1。當有RN個讀者進入讀后,L便減為0,第RN+1 個讀者要進入讀時,必然會因wait(L,1,1)操作失敗而堵塞。對利用信號量來解決讀者—寫者問題的描述如下: Var RN integer;L,mx:semaphore: =RN,1; Begin Parbegin Reader :begin Repeat Swait(L,1,1);Swait(mx,1,0);.Perform reader operation;Ssignal(L,1);Until false;End Writer :begin Repeat Swait(mx ,1,1,l,RN,0);Perform writer operation;Ssignal(mx,1);Until false;End Parend End 其中,Swait(mx,1,0)語句起著開關作用,只要無Writer進程進入些,mx=1,reader進程就都可以進入讀。但是要一旦有Writer進程進入寫時,其MX=0,則任何reader進程就都無法進入讀。Swait(mx ,1,1,l,RN,0)語句表示僅當既無Write進程在寫(mx=1),又無reader進程在讀(L=RN)時,writer進程才能進入臨界區寫。
本設計方案就是通過利用記錄型信號量對讀者寫者問題的解決過程進行模擬演示,形象地闡述記錄型信號量機制的工作原理。
二 設計目的與內容
一 實驗目的
l.用信號量來實現讀者寫者問題。
2.理解和運用信號量、PV原語、進程間的同步互斥關系等基本知識。
二、二 實驗內容
讀者寫者問題的定義如下:有一個許多進程共享的數據區,這個數據區可以是一個文件或者主存的一塊空間;有一些只讀取這個數據區的進程(Reader)和一些只往數據區寫數據的進程(Writer),此外還需要滿足以下條件:
(1)任意多個讀進程可以同時讀這個文件;(2)一次只有一個寫進程可以往文件中寫;
(3)如果一個寫進程正在進行操作,禁止任何讀進程度文件。我們需要分兩種情況實現該問題:
讀優先:要求指一個讀者試圖進行讀操作時,如果這時正有其他讀者在進行操作,他可直接開始讀操作,而不需要等待。
寫優先:一個讀者試圖進行讀操作時,如果有其他寫者在等待進行寫操作或正在進行寫操作,他要等待該寫者完成寫操作后才開始讀操作。
三設計分析
在Windows 7 環境下,創建一個包含n 個線程的控制臺進程。用這n 個線程來表示n個讀者或寫者。每個線程按相應測試數據文件的要求,進行讀寫操作。請用信號量機制分別實現讀者優先和寫者優先的讀者-寫者問題。
讀者-寫者問題的讀寫操作限制:
讀者-寫者的讀寫限制(包括讀者優先和寫者優先)1)寫-寫互斥,即不能有兩個寫者同時進行寫操作
2)讀-寫互斥,即不能同時有一個讀者在讀,同時卻有一個寫者在寫 3)讀讀允許,即可以有2個以上的讀者同時讀
將所有的讀者和所有的寫者分別放進兩個等待隊列中,當讀允許時就讓讀者隊列釋放一個或多個讀者,當寫允許時,釋放第一個寫者操作。讀者寫者問題的定義如下:有一個許多進程共享的數據區,這個數據區可以是一個文件或者主存的一塊空間;有一些只讀取這個數據區的進程(Reader)和一些只往數據區寫數據的進程(Writer),此外還需要滿足以下條件:1)任意多個讀進程可以同時讀這個文件;2)一次只有一個寫進程可以往文件中寫;3)如果一個寫進程正在進行操作,禁止任何讀進程度文件。我們需要分兩種情況實現該問題:
讀優先:要求指一個讀者試圖進行讀操作時,如果這時正有其他讀者在進行操作,他可直接開始讀操作,而不需要等待。寫優先:一個讀者試圖進行讀操作時,如果有其他寫者在等待進行寫操作或正在進行寫操作,他要等待該寫者完成寫操作后才開始讀操作。
四 程序實現
程序由兩部分組成:
1。讀者-寫者模塊:包括系統調用接口,讀者-寫者活動描述主程序。系統接口主要功能是通過管道向父進程發送系統調用命令,并讀取父進程送來的返回值。
讀者-寫者活動程序根據臨界資源的共享,互斥原則編制,具體見源程序。2。主控模塊:主控模塊實現系統初始化系統調用命令接收與解釋執行,系統調用功能的實現(包括信號量機制),及讀者-寫者活動過程記錄與顯示。
初始化系統環境 建立通信管道
啟動讀者-寫者進程 接收系統調用命令 解釋執行
系統初始化模塊 管道建立模塊 進程啟動模塊 命令解釋模塊 Wait()Signal()Wakeup()Block()
五 程序調試
測試數據文件格式: 測試數據文件包括n 行測試數據,分別描述創建的n 個線程是讀者還是寫者,以及讀寫操作的開始時間和持續時間。每行測試數據包括四個字段,各字段間用空格分隔。第一字段為一個正整數,表示線程序號。第二字段表示相應線程角色,R 表示讀者是,W 表示寫者。第三字段為一個正數,表示讀寫操作的開始時間。線程創建后,延時相應時間(單位為秒)后發出對共享資源的讀寫申請。第四字段為一個正數,表示讀寫操作的持續時間。當線程讀寫申請成功后,開始對共享資源的讀寫操作,該操作持續相應時間后結束,并釋放共享資源。
六 結果分析和討論
在讀者寫者同時在隊列中等待申請資時,讀者優先調用資源。而且如果一個讀者申請進行讀操作時已有另一讀者正在進行讀操作,則該讀者可直接開始讀操作,即讀讀允許。
進程1是R操作,在時間3時進入隊列,運行時間是5,在它進入時沒有進程占用資源,它既占用資源;知道它釋放資源,等候的進程有3,4,5;
進程2是W操作,在時間16時進入隊列,運行時間是5,在它進入時進程4占用資源,它等待資源,當4釋放時占用資源;
進程3是R操作,在時間5時進入隊列,運行時間是2,在它進入時進程1占用資源,它等待資源,當進程1釋放資源后,由于讀者優先,進程3,5同時調 運資源;
進程4是R操作,在時間6時進入隊列,運行時間是5,在它進入時進程1占用資源,它等待資源,當進程1釋放資源后,由于讀者優先,進程3,5占用資源,它依然等待,直到進程3,5都結束;
進程5是W操作,在時間4時進入隊列,運行時間是3, 在它進入時進程1占用資源,它等待資源,當進程1釋放資源后,由于讀者優先,進程3,5同時調運資源;
七 心得體會
這一次課程設計,讓我體會很深刻。讀者-寫者問題經典的線程同步問題的一個模型。經過讀者寫者問題的編寫,我對同步機構應用有了深入的了解。懂得了運用信號量實現進程間的互斥。實現了不讓共享資源同時修改。用信號量上的原語操作使臨界段問題的解決比較簡單明了了。讀者寫者問題的編寫,花的時間很多,也學到很多東西。了解支持多道程序的并發操作系統設計中解決資源共享時進程間的同步與互斥的信號量機制。幾天的試驗,雖然難度有點大,但只要自己花時間去學習,還是會攻克困難的。
總之,每一次課程設計不僅是我們學習的好機會,而且是我們鍛煉實際動手能力的平臺,雖然有難度的東西總會讓人很抵觸,比如在課設過程中有很多郁悶的時候,一個小小的錯誤一不小心就花去了自己一上午的時間,所以在這個過程中能夠磨練人的意志與耐心,最后感謝老師的指導與監督。
八 源代碼
#include
第三篇:操作系統課程設計報告——讀者寫者問題
操作系統課程設計
課
題:讀者寫者問題 姓
名:赫前進 班
級:1020552 學
號102055211 指導教師:葉瑤 提交時間:2012/12/30
(一)實驗目的
1.進一步理解 “臨界資源” 的概念;
2.把握在多個進程并發執行過程中對臨界資源訪問時的必要約束條件; 3.理解操作系統原理中 “互斥” 和 “同步” 的涵義。
(二)實驗內容
利用程序設計語言編程,模擬并發執行進程的同步與互斥(要求:進程數目不少于 3 個)。
(三)、程序分析
讀者寫者問題的定義如下:有一個許多進程共享的數據區,這個數據區可以是一個文件或者主存的一塊空間;有一些只讀取這個數據區的進程(Reader)和一些只往數據區寫數據的進程(Writer),此外還需要滿足以下條件:(1)任意多個讀進程可以同時讀這個文件;(2)一次只有一個寫進程可以往文件中寫;
(3)如果一個寫進程正在進行操作,禁止任何讀進程度文件。
實驗要求用信號量來實現讀者寫者問題的調度算法。實驗提供了signal類,該類通過P()、V()兩個方法實現了P、V原語的功能。實驗的任務是修改Creat_Writer()添加寫者進程,Creat_Reader()創建讀者進程。Reader_goon()讀者進程運行函數。讀優先:要求指一個讀者試圖進行讀操作時,如果這時正有其他讀者在進行操作,他可直接開始讀操作,而不需要等待。
讀者優先的附加限制:如果一個讀者申請進行讀操作時已有另一讀者正在進行讀操作,則該讀者可直接開始讀操作。
寫優先:一個讀者試圖進行讀操作時,如果有其他寫者在等待進行寫操作或正在進行寫操作,他要等待該寫者完成寫操作后才開始讀操作。
寫者優先的附加限制:如果一個讀者申請進行讀操作時已有另一寫者在等待訪問共享資源,則該讀者必須等到沒有寫者處于等待狀態后才能開始讀操作。
在Windows 7 環境下,創建一個控制臺進程,此進程包含 n 個線程。用這 n 個線程來表示 n 個讀者或寫者。每個線程按相應測試數據文件(格式見下)的要求進行讀寫操作。用信號量機制分別實現讀者優先和寫者優先的讀者/寫者問題。運行結果顯示要求:要求在每個線程創建、發出讀寫操作申請、開始讀寫操作和結束讀寫操作時分別顯示一行提示信息,以確定所有處理都遵守相應的讀寫操作限制。
測試數據文件包括 n 行測試數據,分別描述創建的 n 個線程是讀者還是寫者,以及讀寫操作的開始時間和持續時間。每行測試數據包括4個字段,各個字段間用空格分隔。
?
第一個字段為一個正整數,表示線程序號
?
第二個字段表示相應線程角色,R 表示讀者,W 表示寫者
?
第三個字段為一個正數,表示讀/寫操作的開始時間:線程創建后,延遲相應時間(單位為秒)后發出對共享資源的讀/寫請求
?
第四個字段為一正數,表示讀/寫操作的持續時間:線程讀寫請求成功后,開始對共享資源的讀/寫操作,該操作持續相應時間后結束,并釋放共享資源 例如: 1 R 3 5 2 W 4 5 3 R 5 2 4 R 6 5 5 W 5.1 3 讀者寫者問題是操作系統中經典的互斥問題:一塊數據被多個讀者和寫者的訪問,需要考慮讀寫互斥、寫寫互斥(可以同時由多個讀者讀取)。具體的又可以分為讀者優先和寫者優先兩類。讀者優先算法:
當新的讀者到來的時候,若當前正有讀者在進行讀操作,則該讀者無需等待前面的寫操作完成,直接進行讀操作。設置兩個互斥信號量:
rwmutex 用于寫者與其他讀者/寫者互斥的訪問共享數據 rmutex 用于讀者互斥的訪問讀者計數器readcount var rwmutex, rmutex : semaphore := 1,1 ; int readcount = 0;cobegin
readeri begin // i=1,2,?.P(rmutex);
Readcount++;
If(readcount == 1)P(rwmutex);
V(rmutex);
讀數據;
P(rmutex);
Readcount--;
If(readcount == 0)V(rwmutex);
V(rmutex);
End
Writerj begin // j = 1,2,?.P(rwmutex);
寫更新;
V(rwmutex);
End Coend 寫者優先: 條件:
1)多個讀者可以同時進行讀
2)寫者必須互斥(只允許一個寫者寫,也不能讀者寫者同時進行)
3)寫者優先于讀者(一旦有寫者,則后續讀者必須等待,喚醒時優先考慮寫者)設置三個互斥信號量:
rwmutex 用于寫者與其他讀者/寫者互斥的訪問共享數據 rmutex 用于讀者互斥的訪問讀者計數器readcount nrmutex 用于寫者等待已進入讀者退出,所有讀者退出前互斥寫操作 var rwmutex, rmutex,nrmutex : semaphore := 1,1,1 ; int readcount = 0;cobegin
readeri begin // i=1,2,?.P(rwmutex);
P(rmutex);
Readcount++;
If(readcount == 1)P(nrmutex);//有讀者進入,互斥寫操作
V(rmutex);
V(rwmutex);// 及時釋放讀寫互斥信號量,允許其它讀、寫進程申請資源
讀數據;
P(rmutex);
Readcount--;
If(readcount == 0)V(nrmutex);//所有讀者退出,允許寫更新
V(rmutex);
End
Writerj begin // j = 1,2,?.P(rwmutex);// 互斥后續其它讀者、寫者
P(nrmutex);//如有讀者正在讀,等待所有讀者讀完
寫更新;
V(nrmutex);//允許后續新的第一個讀者進入后互斥寫操作
V(rwmutex);//允許后續新讀者及其它寫者
End Coend //////////////////////////////////////////////////////////////////////////////////////////////////////////////// /*---------函數聲明---------*/ void Creat_Writer();
//添加一個寫者 void Del_Writer();
//刪除一個寫者 void Creat_Reader();
//添加一個讀者
void Reader_goon();
//讀者進程運行函數 void R_Wakeup();
//喚醒等待讀者 void Del_Reader();
//刪除一個讀者 void Show();
//顯示運行狀態
/*===============
class
signal
===============*/ class signal //信號量對象.{ private: int value;int queue;
//用int型數據模擬等待隊列.public: signal();signal(int n);int P();//檢查臨界資源 int V();//釋放臨界資源 int Get_Value();int Get_Queue();};//////////////////////////////////////////////////////////////////// #include
#include
struct ThreadInfo
{
int num;
char type;
double start;
double time;
}thread_info[MaxThread];
HANDLE hX;
HANDLE hWsem;
HANDLE thread[MaxThread];int readcount;double totaltime;
void WRITEUNIT(int iProcess){
printf(“Thread %d begins to write.n”,iProcess);
Sleep((DWORD)(thread_info[iProcess-1].time*1000));
printf(“End of thread %d for writing.n”,iProcess);}
void READUNIT(int iProcess){
printf(“Thread %d begins to read.n”,iProcess);
Sleep((DWORD)(thread_info[iProcess-1].time*1000));
printf(“End of thread %d for reading.n”,iProcess);}
DWORD
WINAPI
reader(LPVOID
lpVoid){
int iProcess
=
*(int*)lpVoid;
Sleep((DWORD)(thread_info[iProcess-1].start*1000));
DWORD wait_for=WaitForSingleObject(hX,INFINITE);
printf(“Thread %d requres reading.n”,iProcess);
readcount++;
if(readcount==1)WaitForSingleObject(hWsem,INFINITE);
ReleaseMutex(hX);
READUNIT(iProcess);
wait_for=WaitForSingleObject(hX,INFINITE);
readcount--;
if(readcount==0)
ReleaseSemaphore(hWsem,1,0);
ReleaseMutex(hX);
return iProcess;}
DWORD
WINAPI
writer(LPVOID
lpVoid){
int iProcess
=
*(int*)lpVoid;
Sleep((DWORD)(thread_info[iProcess-1].start*1000));
printf(“Thread %d requres writing.n”,iProcess);
DWORD wait_for=WaitForSingleObject(hWsem,INFINITE);
WRITEUNIT(iProcess);
ReleaseSemaphore(hWsem,1,0);
return iProcess;}
int main(){
int threadNum;
int threadcount;
ifstream file;
hX=CreateMutex(NULL, FALSE, NULL);
hWsem=CreateSemaphore(NULL,1,1,NULL);
//???
readcount=0;
threadcount=0;
totaltime=0;
file.open(“thread.dat”,ios::in);
if(file==0)
{
printf(“File Open Error.n”);
return 0;
}
while(file>>threadNum)
{
thread_info[threadNum-1].num=threadNum;
file>>thread_info[threadNum-1].type;
file>>thread_info[threadNum-1].start;
file>>thread_info[threadNum-1].time;
totaltime+=thread_info[threadNum-1].time;
switch(thread_info[threadNum-1].type)
{
case 'W':
printf(“Creating Thread %d writing.n”,thread_info[threadNum-1].num);
thread[threadNum-1]
=
CreateThread(NULL, &thread_info[threadNum-1].num,0,0);
break;
case 'R':
printf(“Creating Thread %d reading.n”,thread_info[threadNum-1].num);
thread[threadNum-1]
=
CreateThread(NULL, &thread_info[threadNum-1].num,0,0);
break;
}
threadcount++;
}
file.close();
Sleep((DWORD)(totaltime*1000));
return 1;} ////////////////////////////////////////////////////////////////////////////////// semaphore fmutex = 1 , rdcntmutex = 1;// fmutex--> access to file;rdcntmutex--> access to readcount int readcount = 0;void reader(){
while(1)
for 0,writer, for 0,reader,{
P(rdcntmutex);
if(readcount==0)
P(fmutex);
readcount = readcount + 1;
V(rdcntmutex);
// Do read operation
P(rdcntmutex);
readcount = readcount1;
if(readcount==0)
V(fmutex);
V(rdcntmutex);
} } void writer(){
while(1)
{
P(wtcntmutex);
if(writecount==0)
P(queue);
writecount = writecount + 1;
V(wtcntmutex);
P(fmutex);
// Do write operation
V(fmutex);
P(wtcntmutex);
writecount = writecount-1;
if(writecount==0)
V(queue);
V(wtcntmutex);
} }
第四篇:操作系統實驗報告
實驗二
進程調度
1.目的和要求
通過這次實驗,理解進程調度的過程,進一步掌握進程狀態的轉變、進程調度的策略,進一步體會多道程序并發執行的特點,并分析具體的調度算法的特點,掌握對系統性能的評價方法。
2.實驗內容
閱讀教材《計算機操作系統》第二章和第三章,掌握進程管理及調度相關概念和原理。
編寫程序模擬實現進程的輪轉法調度過程,模擬程序只對PCB進行相應的調度模擬操作,不需要實際程序。假設初始狀態為:有n個進程處于就緒狀態,有m個進程處于阻塞狀態。采用輪轉法進程調度算法進行調度(調度過程中,假設處于執行狀態的進程不會阻塞),且每過t個時間片系統釋放資源,喚醒處于阻塞隊列隊首的進程。
程序要求如下:
1)輸出系統中進程的調度次序; 2)計算CPU利用率。
3.實驗環境
Windows操作系統、VC++6.0 C語言
4設計思想:
(1)
程序中進程可用PCB表示,其類型描述如下:
struct PCB_type
{
int pid;
//進程名 int
state;
//進程狀態
2——表示“執行”狀態
1——表示“就緒”狀態
0——表示“阻塞”狀態
int cpu_time;//運行需要的CPU時間(需運行的時間片個數)
} 用PCB來模擬進程;
(2)設置兩個隊列,將處于“就緒”狀態的進程PCB掛在隊列ready中;將處于“阻塞”狀態的進程PCB掛在隊列blocked中。隊列類型描述如下:
struct QueueNode{
struct PCB_type
PCB;
Struct QueueNode *next;} 并設全程量:
struct QueueNode *ready_head=NULL,//ready隊列隊首指針
*ready_tail=NULL , //ready隊列隊尾指針
*blocked_head=NULL,//blocked隊列隊首指針 *blocked_tail=NULL;//blocked隊列隊尾指針(3)設計子程序:
start_state();
讀入假設的數據,設置系統初始狀態,即初始化就緒隊列和阻塞隊列。
dispath();
模擬調度,當就緒隊列的隊首進程運行一個時間片后,放到就緒隊列末尾,每次都是隊首進程進行調度,一個進程運行結束就從就緒隊列中刪除,當到t個時間片后,喚醒阻塞隊列隊首進程。
calculate();
就緒進程運行一次,usecpu加1,當就緒隊列為空時unusecpu加1,CPU利用率為use_cpu/(use_cpu+unuse_cpu)。
5源代碼:
#include
struct PCB_type {
int pid;
//進程名
int
state;
//進程狀態
//2--表示“執行”狀態
//1--表示“就緒”狀態
//0--表示“阻塞”狀態
int cpu_time;//運行需要的CPU時間(需運行的時間片個數)};struct QueueNode{
struct PCB_type
PCB;
struct QueueNode *next;};struct QueueNode *ready_head=NULL,//ready隊列隊首指針
*ready_tail=NULL,//ready隊列隊尾指針
*block_head=NULL,//blocked隊列隊首指針
*block_tail=NULL;
//blocked隊列隊尾指針
int use_cpu,unuse_cpu;
void start_state()//讀入假設的數據,設置系統初始狀態 {
int n,m;
int i;
struct QueueNode *p,*q;
printf(“輸入就緒節點個數n:”);
scanf(“%d”,&n);
printf(“輸入阻塞節點個數m:”);
scanf(“%d”,&m);
p=(struct QueueNode *)malloc(sizeof(struct QueueNode));
p->next =NULL;
ready_head=ready_tail=p;
for(i=0;i { p=(struct QueueNode *)malloc(sizeof(struct QueueNode)); p->next =NULL; p->PCB.state=1; printf(“輸入就緒進程%d的pid和cpu_time:”,i+1); scanf(“%d%d”,&p->PCB.pid,&p->PCB.cpu_time); ready_tail->next=p; ready_tail=p; } q=(struct QueueNode *)malloc(sizeof(struct QueueNode)); q->next =NULL; block_head=block_tail=q; for(i=0;i { q=(struct QueueNode *)malloc(sizeof(struct QueueNode)); q->next=NULL; q->PCB.state=0; printf(“輸入阻塞進程%d的pid和cpu_time:”,i+1); scanf(“%d%d”,&q->PCB.pid,&q->PCB.cpu_time); block_tail->next=q; block_tail=q; } printf(“n處于就緒狀態的進程有:n”); p=ready_head->next; i=1; while(p) {printf(“進程%d的pid和cpu_time:%5d%5d%5dn“,i,p->PCB.pid,p->PCB.state,p->PCB.cpu_time); p=p->next; i++; } } void dispath() //模擬調度 { int x=0,t; use_cpu=0; unuse_cpu=0; printf(”輸入t:“); scanf(”%d“,&t); printf(”開始調度n“); while(ready_head!=ready_tail||block_head!=block_tail) { struct QueueNode *p,*q; if(ready_head!=ready_tail) { p=ready_head->next; ready_head->next=p->next; p->next=NULL; if(ready_head->next==NULL) { ready_tail=ready_head; } p->PCB.state=2; printf(”進程%d調度t“,p->PCB.pid); state和 use_cpu++; x++; p->PCB.cpu_time--; if(p->PCB.cpu_time) { ready_tail->next=p; ready_tail=p; } else { printf(”進程%d完成t“,p->PCB.pid); free(p); } } else { unuse_cpu++; x++; printf(”空閑一個時間片t“); } if(x==t&&block_head!=block_tail) { q=block_head->next; block_head->next=q->next; q->next=NULL; if(block_head->next==NULL) { block_tail=block_head; } ready_tail->next=q; ready_tail=q; x=0; } } } void calculate() //計算CPU利用率 { printf(”ncpu的利用率%.2fn“,(float)use_cpu/(use_cpu+unuse_cpu)); } void main(){start_state(); dispath(); calculate();} 6運行結果: 7實驗總結: 實驗幫我復習了數據結構和C語言,且鞏固課本知識,知道了如何定義結構體,如何在鏈接隊列中增刪節點。模擬進程調度幫我們鞏固了進程三狀態之間的變遷。懂得調式的重要性。總之,我們明白了理論聯系實際。多看書,多上機。 實驗三 可變分區存儲管理 1.目的和要求 通過這次實驗,加深對內存管理的認識,進一步掌握內存的分配、回收算法的思想。 2.實驗內容 閱讀教材《計算機操作系統》第四章,掌握存儲器管理相關概念和原理。編寫程序模擬實現內存的動態分區法存儲管理。內存空閑區使用自由鏈管理,采用最壞適應算法從自由鏈中尋找空閑區進行分配,內存回收時假定不做與相鄰空閑區的合并。 假定系統的內存共640K,初始狀態為操作系統本身占用64K。在t1時間之后,有作業A、B、C、D分別請求8K、16K、64K、124K的內存空間;在t2時間之后,作業C完成;在t3時間之后,作業E請求50K的內存空間;在t4時間之后,作業D完成。要求編程序分別輸出t1、t2、t3、t4時刻內存的空閑區的狀態。 3.實驗環境 Windows操作系統、VC++6.0 C語言 4.設計思想 模擬內存分配和回收,要設置兩個鏈隊列,一個空閑區鏈和一個占用區鏈,空閑區鏈節點有起始地址,大小和指向下一節點的指針等數據域,占用區鏈節點有起始地址,大小,作業名和指向下一節點的指針等數據域,本實驗用最壞適應算法,每次作業申請內存都是從空閑鏈隊頭節點分配,如果相等,就刪除空閑頭結點,如果小于申請的,就不分配,否則就劃分內存給作業,剩下的內存大小,重新插入空閑鏈隊,按從大到小,接著把作業占用的內存放到占用區鏈節點的末尾。每次作業運行完,就要回收其占用的內存大小,把作業節點按從大到小插入到空閑鏈隊中。5.源代碼: #include struct freelinkNode *next;};struct busylinkNode{ char name; int len;int address;struct busylinkNode *next;};struct freelinkNode *free_head=NULL; //自由鏈隊列(帶頭結點)隊首指針 struct busylinkNode *busy_head=NULL; //占用區隊列隊(帶頭結點)首指針 struct busylinkNode *busy_tail=NULL; //占用區隊列隊尾指針 void start(void)/* 設置系統初始狀態*/ { struct freelinkNode *p; struct busylinkNode *q; free_head=(struct freelinkNode*)malloc(sizeof(struct freelinkNode)); free_head->next=NULL;// 創建自由鏈頭結點 busy_head=busy_tail=(struct busylinkNode*)malloc(sizeof(struct busylinkNode)); busy_head->next=NULL;// 創建占用鏈頭結點 p=(struct freelinkNode *)malloc(sizeof(struct freelinkNode)); p->address=64; p->len=640-64;//OS占用了64K p->next=NULL; free_head->next=p; q=(struct busylinkNode *)malloc(sizeof(struct busylinkNode)); q->name='S';/* S表示操作系統占用 */ q->len=64;q->address=0;q->next=NULL; busy_head->next=q;busy_tail=q;} void requireMemo(char name, int require)/*模擬內存分配*/ { freelinkNode *w,*u,*v;busylinkNode *p;if(free_head->next->len>=require){ p=(struct busylinkNode*)malloc(sizeof(struct busylinkNode)); p->name=name; p->address=free_head->next->address; p->len=require; p->next=NULL; busy_tail->next=p; busy_tail=p;} else printf(”Can't allocate“); w=free_head->next; free_head->next=w->next; if(w->len==require) { free(w);} else { w->address=w->address+require; w->len=w->len-require;} u=free_head; v=free_head->next; while((v!=NULL)&&(v->len>w->len)){ u=v; v=v->next;} u->next=w; w->next=v;} void freeMemo(char name)/* 模擬內存回收*/ { int len; int address;busylinkNode *q,*p;freelinkNode *w,*u,*v;q=busy_head; p=busy_head->next; while((p!=NULL)&&(p->name!=name)) { q=p; p=p->next;} if(p==NULL){ printf(”%c is not exist“,name);} else { if(p==busy_tail) { busy_tail=q; } else { q->next=p->next; len=p->len; address=p->address; free(p); w=(struct freelinkNode*)malloc(sizeof(struct freelinkNode)); w->len=len; w->address=address; u=free_head; v=free_head->next; while((v!=NULL)&&(v->len>len)) { u=v;v=v->next; } u->next=w; w->next=v; } } } void past(int time)/* 模擬系統過了time 時間*/ { printf(”過了時間%d后:n“,time);} void printlink()/* 輸出內存空閑情況(自由鏈的結點)*/ { freelinkNode *p; printf(”內存的空閑情況為:n“); p=(struct freelinkNode *)malloc(sizeof(struct freelinkNode)); p=free_head->next; while(p!=NULL) { printf(”內存的起始地址和內存的大小%5dt%5d:n",p->address,p->len); p=p->next; } } void main(){ int t1=1,t2=2,t3=3,t4=4; start(); past(t1); requireMemo('A',8); requireMemo('B',16); requireMemo('C',64); requireMemo('D',124); printlink(); past(t2); freeMemo('C'); printlink(); past(t3); requireMemo('E',50); printlink(); past(t4); freeMemo('D'); printlink();} 6.運行結果: 7.實驗總結: 鞏固編程能力,和調式能力,復習課本知識,明白理論聯系實際的重要性,動手能力非常重要,多看書,多獨立思考,品味痛苦的過程,享受成功的喜悅。 操作系統實驗報告 院系:數計學院 班級:大類6班 學號:100511624 姓名:明章輝 指導教師:徐軍利 許昌學院 《操作系統》實驗報告書 學號:姓名:閆金科班級:成績: 5006140057 14物聯網工程 2016年02月實驗一 Linux的安裝與配置 一、實驗目的 1.熟悉Linux系統的基本概念,比如Linux發行版、宏內核、微內核等。2.掌握Linux系統的安裝和配置過程,初步掌握Linux系統的啟動和退出方法。3.熟悉Linux系統的文件系統結構,了解Linux常用文件夾的作用。 二、實驗內容 1.從網絡上下載VMware軟件和兩個不同Linux發行版鏡像文件。2.安裝VMware虛擬機軟件。 3.在VMware中利用第一個鏡像文件完成第一個Linux的安裝,期間完成網絡信息、用戶信息、文件系統和硬盤分區等配置。 4.在VMware中利用第二個鏡像文件完成第二個Linux的安裝,并通過LILO或者GRUB解決兩個操作系統選擇啟動的問題。 5.啟動Linux系統,打開文件瀏覽器查看Linux系統的文件結構,并列舉出Linux常用目錄的作用。 三、實驗過程及結果 1、啟動VMware,點擊新建Linux虛擬機,如圖所示: 2、點擊下一步,選擇經典型,點擊下一步在選擇客戶機頁面選擇Linux,版本選擇Red Hat Enterprise Linux 5,如圖所示: 3、點擊下一步創建虛擬機名稱以及所要安裝的位置,如圖所示: 4、點擊下一步,磁盤容量填一個合適大小,此處選擇默認值大小10GB,如圖所示: 5、點擊完成,點擊編輯虛擬機設置,選擇硬件選項中的CD-ROM(IDE...)選項,在右側連接中選擇“使用ISO鏡像(I)”選項,點擊“瀏覽”,找到Linux的鏡像文件,如圖所示: 6點擊確定按鈕后,點擊啟動虛擬機按鈕,來到Linux的安裝界面,如圖所示: 7、到此頁面之后,等待自動檢測安裝,如圖所示: 8、等到出現如圖所示頁面后點擊“skip”按鈕,跳過檢測,直接進入安裝設置界面,如圖所示: 9、安裝設計界面如圖所示: 10、點擊Next按鈕進入設置語言界面,設置語言為“簡體中文”,如圖所示: 11、點擊Nest按鈕進入系統鍵盤設置按鈕,設置系統鍵盤為“美國英語式”,如圖所示: 12、點擊下一步按鈕,彈出“安裝號碼”對話框,選擇跳過輸入安裝號碼,如圖所示: 13、按照提示,一直點擊下一步按鈕,如圖所示: 14、到設置最后一步,點擊下一步按鈕進入開始安裝Red Hat Enterprise Linux Sever界面,如圖所示: 15、安裝完成后,進入歡迎界面,按照提示點擊前進按鈕知道進入Linux桌面,如圖所示: 16、安裝成功的Linux系統桌面如圖所示,桌面包含五個圖標,分別為:計算機、jk’s Home、回收站、RHEL/5.3 i386DVD。 四、實驗總結 通過安裝虛擬機等操作讓我認識到Linux這系統一些基本特點,本次試驗學會了安裝虛擬機并且使用虛擬機安裝操作系統,掌握了紅帽Linux系統的安裝和配置過程,以及對鏡像ISO文件的使用,有別于我們機器上使用的系統,通過虛擬機這個軟件還可以在已有系統的基礎上使用其他操作系統。安裝過程中一定要注意選擇版本的時候要選擇Red Hat Enterprise Linux 5版本,否則安裝不能成功。自己動手成功的安裝了Linux系統,自己對Linux的學習產生更大的興趣。 實驗二 Linux操作系統的運行模式 一、實驗目的 1.熟悉Linux系統終端工作環境的使用,了解Linux命令的格式,使用學會利用常用的Linux命令來完成系統的管理和維護。 2.了解X-Windows的特點,熟悉Linux圖形用戶接口的使用,掌握GNOME桌面環境的基本操作。 3.了解和掌握在Linux環境下安裝軟件包的方法,如QQ for Linux等用軟件的安裝方法。 二、實驗內容 1.啟動Linux系統打開虛擬終端界面,使用Linux的在線幫助指令man或help獲得ls、uname、date、cal、mkdir、cp等Linux命令的幫助手冊,了解這些命令的具體使用方法。同時,也可以通過執行“命令名 –help”來顯示該命令的幫助信息,如“ls –help”,試用這些命令。 2.通過uname命令的執行,查看并給出相關系統信息:操作系統的名稱、系統域名、系統CPU名稱等。 3.在主目錄下創建一個名為myetc的子目錄,將/etc目錄下與網絡相關的文件和子目錄拷貝到該目錄,并將這些文件的執行權限設置為可執行。 4.在主目錄/home下創建目錄program、music 和temp,然后在program下建立目錄java和C,列出完成該過程的所有命令。 5.在圖形界面環境中,查看GNOME桌面的面板和桌面,設置GNOME,包括屏幕保護程序、更改背景和指定關聯程序等。6.實現對光盤的加載和訪問,然后卸載。 三、實驗過程及結果 1、打開終端,輸入 【ls –help】來查看【ls】指令的使用方法,同理查看uname、date、cal、mkdir、cp的使用方法。 2、在終端中輸入【uname –a】顯示操作系統名系統cpu名和系統域名 3、重啟系統,用【root】用戶名進入系統,以獲得權限。在終端中輸入【mkdir myetc】,在主目錄下創建【myrtc】的目錄,【ls】查看是否創建。輸入【cd..】返回至【/】文件,輸入【cp –r etc root/myetc】講etc中內容復制到myetc中,進入myetc文件【ls】查看。輸入 【chmod u+x etc】賦予文件可執行的權限,輸入【ll】查看。 4、在home下,輸入【mkdir {program,music,temp}】,可在home下創立這三個目錄,輸入【ls】查看。在program下輸入【mkdir{java,C}】,可創立java和C兩個目錄,【ls】查看。 5、在桌面上方選擇【系統】-【首選項】,即可設置屏幕保護程序和更改背景和指定關聯程序 5、在桌面上可見看到有CD光盤,雙擊瀏覽,右鍵【彈出】即卸載。 四、實驗總結和體會 Linux的指令系統是學習Linux操作系統很重要的一部分,指令系統相當于在Windows操作系統下的doc,可以省去圖形化界面。通過這次的實驗讓我了解了Linux的強大功能,了解到Linux有許多方便快捷的設置基本配置的方法,這使我更喜歡上Linux的使用。在使用指令的過程中,有時候對文件的操作需要一定的權限,這時需要在登陸時用戶名使用【root】,而不是我們在安裝時使用的用戶名,這樣就獲得了管理員權限,可以對一些系統文件進行操作。 實驗三 Linux應用軟件與系統管理 一、實驗目的 1.了解OpenOffice.Org集成辦公軟件,掌握利用OpenOffice.Org的套件來完成文檔和圖片的處理。 2.了解Linux網絡管理的知識,熟悉Linux網絡配置的方法,掌握在Linux環境下配置Web服務器和ftp服務的方法。 二、實驗內容 1.配置Linux系統的網絡環境,安裝FTP和Web服務器,并配置相關的屬性,利用FTP實現WINDOWS和Linux之間的數據交換。 2.利用FTP程序上傳自己的照片到FTP服務器,利用OpenOffice的文字處理工具OpenOffice Writer制作一份表格形式的個人簡歷。個人簡歷中至少包含學號、姓名、性別、專業、照片和學習經歷等內容,并保存為網頁格式(html格式)。3.將個人簡歷網頁設置為WEB服務器的首頁,然后在客戶端利用瀏覽器訪問WEB服務器,查看效果。 4.通過讀取proc文件系統,獲取系統各種信息(如主機名、系統啟動時間、運行時間、版本號、所有進程信息、CPU使用率等),并以比較容易的方式顯示。 三、實驗過程及結果 1.配置網絡環境:在(服務.cmd).里面進行以下操作:在服務里選擇3按回車 完成后,可在本地連接看到VMware已連接上網絡 在虛擬機設置中設置以太網網絡連接方式為 網關地址填虛擬機的網管,IP地址設為虛擬機的一個子網: 四、總結: 在linux系統下,make是我們經常用到的編譯命令,所以關于make代碼和他的操作指令一定要記清楚。所以,熟練掌握了make和makefile工具之后,源碼安裝軟件就變的像windows下安裝軟件一樣簡單。 實驗四 進程控制與管理 一、實驗目的 1.掌握GCC編譯器的用法,學會利用GCC編輯器來編輯C語言程序,學會利用GDB調試器來調試C語言程序。 2.理解進程和程序的區別和聯系,3.掌握在Linux環境下觀察進程運行情況和CPU工作情況的命令。4.了解fork()系統調用,掌握利用fork()創建進程的方法。 5.了解Linux系統其他與進程相關的系統調用,如exec、wait和exit等。6.了解Linux常用的進程通信機制。 二、實驗內容 1.利用Linux的進程管理命令ps、top來監視和跟蹤進程,體會進程和程序的關系。2.利用Linux的文字編輯器編寫文件復制的C語言程序,并用gcc編譯該程序,然后運行該程序。 3.編寫一段程序,使用系統調用fork()創建兩個子進程。當此程序運行時,在系統中有一個父進程和兩個子進程活動。讓每一個進程在屏幕上顯示一個字符:父進程顯示'a',子進程分別顯示字符'b'和字符'c'。試觀察記錄屏幕上的顯示結果,并分析原因。 4.修改上述程序,每一個進程循環顯示一句話。子進程顯示'daughter ?'及'son ??',父進程顯示 'parent ??',觀察結果,分析原因。5.用fork()創建一個進程,再調用exec()用新的程序替換該子進程的內容。 三、實驗過程及結果 1、利用Linux的進程管理命令ps、top來監視和跟蹤進程,體會進程和程序的關系。<1>從用戶身份切換到ROOT身份 <2>輸入命令 ps 查看進程 <2>輸入命令 top 跟蹤進程 2、利用Linux的文字編輯器編寫一個計算機100個自然數和的C語言程序,并用gcc編譯該程序,然后運行該程序。 <1>創建一個.C文件 并進入進行編輯 <2>用GCC 進行編譯,再查看文件,發現產生執行文件 a.out <3>執行這個可執行文件得到結果5050 1、編寫一段程序,使用系統調用fork()創建兩個子進程。當此程序運行時,在系統中有一個父進程和兩個子進程活動。讓每一個進程在屏幕上顯示一個字符:父進程顯示'a',子進程分別顯示字符'b'和字符'c'。試觀察記錄屏幕上的顯示結果,并分析原因。 <1>穿件一個.C文件 并進行編寫程序代碼 <2>反復執行2次該程序 <3>可以看出兩次執行的結果 a b c 出現的順序不同,原因是,3個進程的輸出次序是隨機的,并不會按規定的順序出現,所以會出現上述結果。 4、修改上述程序,每一個進程循環顯示一句話。子進程顯示'daughter ?'及'son ??',父進程顯示 'parent ??',觀察結果,分析原因。<1>重新修改代碼 <3>執行這段程序 <4>原分析: 因和之前一樣,可以看出執行的結果 3個單詞出現的順序不同,原因是,3個進程的輸出次序是隨機的,并不會按規定的順序出現,所以會出現上述結果。 5、用fork()創建一個進程,再調用exec()用新的程序替換該子進程的內容。<1> 編寫代碼 <2> 執行的結果 結果表明 execl 替代了son的內容 四、實驗總結和體會 這個實驗考察的是進程之間存在很多可能性以及對編輯器的使用。本次實驗學習了在linux環境下用gcc編譯器運行c語言程序,在linux環境下編寫程序用到了vi編輯器,知道了該編輯器也需要各種命令來操作。編寫C語言程序時用到了fork()函數,再調用execl()用新的程序替換該子進程的內容。 實驗五 進程調度模擬程序的設計與實現 一、實驗目的 1.了解進程調度的概念,掌握常用進程調度算法的原理。2.掌握Linux程序設計編輯、編譯和調試的技巧。 二、實驗內容 1.編寫程序實現進程調度調度算法先來先服務、優先級高優先和時間片輪轉調度算法。(編程語言不限) 2.輸入數據,輸出運行結果。 三、實驗過程及結果 1先來先服務 #i nclude struct { int id; float ArriveTime;float RequestTime;float StartTime;float EndTime;float RunTime;float DQRunTime;int Status;}arrayTask[4];GetTask(){ int i;float a; for(i=0;i<4;i++){arrayTask[i].id=i+1;printf(“input the number”); printf(“input the the ArriveTime of arrayTask[%d]:”,i);scanf(“%f”,&a); arrayTask[i].ArriveTime=a; printf(“input the RequestTime of arrayTask[%d]:”,i);scanf(“%f”,&a); arrayTask[i].RequestTime=a;arrayTask[i].StartTime=0;arrayTask[i].EndTime=0;arrayTask[i].RunTime=0;arrayTask[i].Status=0; } } int fcfs() { int i,j,w=0; for(i=0;i<4;i++) { if(arrayTask[i].Status==0) { t=arrayTask[i].ArriveTime; w=1; } if(w==1) break; } for(i=0;i<4;i++) { if(arrayTask[i].ArriveTime t=arrayTask[i].ArriveTime; } for(i=0;i<4;i++) { if(arrayTask[i].ArriveTime==t) return i; } } int sjf(){ int i,x=0,a=0,b=0;float g; for(i=0;i<4;i++){ if(arrayTask[i].Status==1){g=arrayTask[i].EndTime;x=1;} } if(x==0){ t=arrayTask[0].ArriveTime; for(i=0;i<4;i++){ if(arrayTask[i].ArriveTime t=arrayTask[i].ArriveTime;a=i;} } return a;} else { for(i=0;i<4;i++){ if(arrayTask[i].EndTime>g)g=arrayTask[i].EndTime;} for(i=0;i<4;i++){ if(arrayTask[i].Status==0&& arrayTask[i].ArriveTime<=g){ t=arrayTask[i].RequestTime;a=i;b=1;} /*判斷有沒有進程在前個進程完成前到達*/ } if(b!=0)/*有進程到達則按SJF*/ { for(i=0;i<4;i++){ if(arrayTask[i].Status==0&&arrayTask[i].ArriveTime<=g&&arrayTask[i].RequestTime return a;} else{ /*否則按FCFS*/ for(i=0;i<4;i++) {if(arrayTask[i].Status==0)t=arrayTask[i].ArriveTime;} for(i=0;i<4;i++){ if(arrayTask[i].Status==0&&arrayTask[i].ArriveTime return a;} } } new(int s)/*定義執行進程后相關數據的修改*/ { int i,g=0;for(i=0;i<4;i++){ if(arrayTask[i].Status==0)continue;else { g=1;break;} } if(g==0)/*當處理的是第一個未執行的進程時執行*/ { arrayTask[s].StartTime=arrayTask[s].ArriveTime; arrayTask[s].EndTime=arrayTask[s].RequestTime+arrayTask[s].ArriveTime;arrayTask[s].RunTime=arrayTask[s].RequestTime;arrayTask[s].Status=1;g=2;} if(g==1)/*當處理的不是第一個未執行的進程時執行*/ { arrayTask[s].Status=1;for(i=0;i<4;i++){ if(arrayTask[i].Status==1)d=arrayTask[i].EndTime;} for(i=0;i<4;i++)/*查找最后執行的進程的完成時間*/ { if(arrayTask[i].EndTime>d&&arrayTask[i].Status==1)d=arrayTask[i].EndTime;} if(arrayTask[s].ArriveTime arrayTask[s].StartTime=arrayTask[s].ArriveTime; arrayTask[s].EndTime=arrayTask[s].StartTime+arrayTask[s].RequestTime;arrayTask[s].RunTime=arrayTask[s].EndTime-arrayTask[s].ArriveTime;} arrayTask[s].DQRunTime=arrayTask[s].RunTime/arrayTask[s].RequestTime;} Printresult(int j)/*定義打印函數*/ { printf(“%dt”,arrayTask[j].id); printf(“%5.2ft”,arrayTask[j].ArriveTime);printf(“%5.2ft”,arrayTask[j].RequestTime);printf(“%5.2ft”,arrayTask[j].StartTime);printf(“%5.2ft”,arrayTask[j].EndTime);printf(“%5.2ft”,arrayTask[j].RunTime);printf(“%5.2fn”,arrayTask[j].DQRunTime);} main(){ int i,b,k,a,c=0;int d[4];clrscr(); printf(“t F.FCFS n”);printf(“t S.SFJ n”);printf(“t Q.EXIT n”);for(i=0;;i++){ if(c)break; printf(“please input the number a:n”);scanf(“%d”,&a);switch(a){ case Q: c=1;break; case F:printf(“please input the different-ArriveTime of arrayTasksn”);GetTask(); printf(“*****************************the result of fcfsn”);printf(“NumbertArrivetServertStarttFinishtTurnovetTake power turnover timen”); for(b=0;b<4;b++)/*調用兩個函數改變結構體數的值*/ { k=fcfs();d[b]=k;new(k);} for(b=0;b<4;b++) Printresult(d[b]);/*調用打印函數打出結果*/ continue; case S: printf(“please input the different-RequestTime of array Tasksn”);GetTask(); printf(“******************************the result of sjfn”);printf(“NumbertArrivetRequesttStarttEndtRuntDQRun timen”);for(b=0;b<4;b++){ k=sjf();d[b]=k;new(k);} for(b=0;b<4;b++)Printresult(d[b]);continue; default:printf(“the number Error.please input another number!n”);} } } 四、實驗總結和體會 通過做本實驗,讓我對進程或作業先來先服務、高優先權、按時間片輪轉調度算法以及進程調度的概念和算法,有了更深入的認識!理解進程的狀態及變化,動態顯示每個進程的當前狀態及進程的調度情況。進程調度是處理機管理的核心內容。優先級高優先是根據作業的優先級,總是選擇優先級最高者進入隊列。輪轉調度算法是調度程序每次把CPU分配給就緒隊列首進程/線程使用規定的時間間隔,就緒隊列中都路保留巡行一個時間片。第五篇:操作系統實驗報告