操作系統(tǒng)原理 實(shí)驗(yàn)4、5 并發(fā)與調(diào)度、存儲管理_第1頁
操作系統(tǒng)原理 實(shí)驗(yàn)4、5 并發(fā)與調(diào)度、存儲管理_第2頁
操作系統(tǒng)原理 實(shí)驗(yàn)4、5 并發(fā)與調(diào)度、存儲管理_第3頁
操作系統(tǒng)原理 實(shí)驗(yàn)4、5 并發(fā)與調(diào)度、存儲管理_第4頁
操作系統(tǒng)原理 實(shí)驗(yàn)4、5 并發(fā)與調(diào)度、存儲管理_第5頁
已閱讀5頁,還剩63頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

實(shí)驗(yàn)4

并發(fā)與調(diào)度

4.1WindowsXP線程同步

4.1.1WindowsXP的線程同步

WindowsXP提供的常用對象可分成3類:核心應(yīng)用服務(wù)、線程同步和線程間通

訊。其中,開發(fā)人員可以使用線程同步對象來協(xié)調(diào)線程和進(jìn)程的工作,以使其共享

信息并執(zhí)行任務(wù)。此類對象包括互鎖數(shù)據(jù)、臨界段、事件、互斥體和信號等。

多線程編程中關(guān)鍵的一步是保護(hù)所有的共享資源,工具主要有互鎖函數(shù)、臨界

段和互斥體等:另一個實(shí)質(zhì)性部分是協(xié)調(diào)線程使其完成應(yīng)用程序的任務(wù),為此,可

利用內(nèi)核中的事件對象和信號。

在進(jìn)程內(nèi)或進(jìn)程間實(shí)現(xiàn)線程同步的最方便的方法是使用事件對象,這一組內(nèi)核

對象允許一個線程對其受信狀態(tài)進(jìn)行直接控制(見表4.1)。

而互斥體則是另一個可命名且安全的內(nèi)核對象,其主要目的是引導(dǎo)對共享資源

的訪問。擁有單一訪問資源的線程創(chuàng)建互斥體,所有想要訪問該資源的線程應(yīng)該在

實(shí)際執(zhí)行操作之前獲得互斥體,而在訪問結(jié)束時立即釋放互斥體,以允許下一個等

待線程獲得互斥體,然后接著進(jìn)行下去。

與事件對象類似,互斥體容易創(chuàng)建、打開、使用并清除。利用CreateMulex。API

可創(chuàng)建互斥體,創(chuàng)建時還可以指定一個初始的擁有權(quán)標(biāo)志,通過使用這個標(biāo)志,只

有當(dāng)線程完成了資源的所有的初始化工作時,才允許創(chuàng)建線程釋放互斥體。

86操作系統(tǒng)原理實(shí)驗(yàn)(第二版)

表4.1用于管理事件對象的API

API名稱描述

在內(nèi)核中創(chuàng)建一個新的事件對象。此函數(shù)允許有安全性設(shè)置、手工還是

CreateEvent()

自動丞置的標(biāo)志以及初始時已接受還是未接受信號狀態(tài)的標(biāo)志

創(chuàng)建對己經(jīng)存在的事件對象的引用。此API函數(shù)需要名稱、繼承標(biāo)志和

OpenEventO

所需的訪問級別

SctEvcntO將手工重置事件轉(zhuǎn)化為已接受信號狀態(tài)

ResetEvent()將手工重置事件轉(zhuǎn)化為非接受信號狀態(tài)

將自動重置事件對象轉(zhuǎn)化為已接受信號狀態(tài)。當(dāng)系統(tǒng)釋放所有的等待它

PulseEventO

的線程時此種轉(zhuǎn)化立即發(fā)生

為了獲得互斥體,首先,想要訪問調(diào)用的線程可使用OpenMutexOAPI來獲得指

向?qū)ο蟮木浔?;然后,線程將這個句柄提供給一個等待函數(shù)。當(dāng)內(nèi)核將互斥體對象

發(fā)送給等待線程時,就表明該線程獲得了互斥體的擁有權(quán)。當(dāng)線程獲得擁有權(quán)時,

線程控制了對共享資源的訪問一一必須設(shè)法盡快地放棄互斥體。放棄共享資源時需

要在該對象上調(diào)用RcleaseMutcOAPI。然后系統(tǒng)負(fù)責(zé)將互斥體擁有權(quán)傳遞給下?個等

待著的線程(由到達(dá)時間決定順序)。

4.1.2練習(xí)與實(shí)驗(yàn)

在本實(shí)驗(yàn)中,通過對尋件和互斥體對象的了解,來加深對WindowsXP線程同步

的理解。

1)回顧系統(tǒng)進(jìn)程、線程的有關(guān)概念,加深對WindowsXP線程的理解。

2)了解事件和互斥體對象。

3)逋過分析實(shí)驗(yàn)程序,了解管埋事件對象的APE

4)了解在進(jìn)程中如何使用事件對象。

5)了解在進(jìn)程中如何使用互斥體對象。

6)了解父進(jìn)程創(chuàng)建子進(jìn)程的程序設(shè)計方法。

1.工具/準(zhǔn)備工作

在開始本實(shí)驗(yàn)之前,請回顧教科書的相關(guān)內(nèi)容。

需要準(zhǔn)備一臺運(yùn)行WindowsXPProfessional操作系統(tǒng)的計算機(jī),且該計算機(jī)中

需安裝VisualC++6.0專業(yè)版或企業(yè)版。

2.實(shí)驗(yàn)內(nèi)容與步驟

(1)事件對象

清單4-1程序展示了如何在進(jìn)程間使用事件。父進(jìn)程啟動時,利用CreateEvenK)

API創(chuàng)建一個命名的、可共享的事件和子進(jìn)程,然后等待子進(jìn)程向事件發(fā)出信號并終

止父進(jìn)程。在創(chuàng)建時,子進(jìn)程通過OpcnEvent。API打開事件對象,調(diào)用SctEvcnt()API

使其轉(zhuǎn)化為已接受信號狀態(tài)。兩個進(jìn)程在發(fā)出信號之后幾乎立即終止。

步驟1:登錄進(jìn)入WindowsXPProfessionalo

步驟2:在“開始”菜單中單擊MicrosoftVisualC++6.0命令,進(jìn)入VisualC++

窗口。

步驟3:編輯實(shí)驗(yàn)源程序4-1.cpp(也可直接打開下載的源程序文件4-1.cpp)。

清單47創(chuàng)建和打開事件對象在進(jìn)程間傳送信號

//event項(xiàng)目

#include<windows.h>

#include<iostream>

//以下是句柄事件。實(shí)際中很可能使用共享的包含文件來進(jìn)行通訊

staticLPCTSTRg_szContinueEvent="w2kdg.EventDemo.event.Continue":

//本方法只是創(chuàng)建了一個進(jìn)程的副本,以子進(jìn)程模式(由命令行指定)工作

BOOLCreateChildO

(

//提取當(dāng)前可執(zhí)行文件的文件名

TCHARszFilename[MAX_PATH];

::GetModuleFileName(NULL,szFilename,MAXPATH);

//格式化用于子進(jìn)程的命令行,指明它是一個EXE文件和子進(jìn)程

TCHARszCmdLine[MAX_PATH];

::sprintf(szCmdLine,為szFilename);

//子進(jìn)程的啟動信息結(jié)構(gòu)

STARTUPINFOsi;

::ZeroMemory(reinterpret_cast<void*>(&si),sizeof(si));

si.cb=sizeof(si);//必須是本結(jié)構(gòu)的大小

88操作系統(tǒng)原理實(shí)驗(yàn)(第二版)

//返回的子進(jìn)程¥J進(jìn)程信息結(jié)構(gòu)

PROCESS.INFORMATIONpi;

//使用同一可執(zhí)行文件和告訴它是一個子進(jìn)程的命令行創(chuàng)建進(jìn)程

BOOLbCreateOK=::CreateProcess(

szFilenamG,//生成的可執(zhí)行文件名

szCmdLine,//指示其行為與子進(jìn)程一樣的標(biāo)志

NULL,//子進(jìn)程句柄的安全性

NULL,//子線程句柄的安全性

FALSE,//不繼承句柄

o,//特殊的創(chuàng)建標(biāo)志

NULL,//新環(huán)境

NULL,//當(dāng)前目錄

&si,//啟動信息結(jié)構(gòu)

&pi);//返回的進(jìn)程信息結(jié)構(gòu)

//釋放對子進(jìn)程的引用

if(bCreateOK)

(

::CloseHandle(pi.hProcess);

::CloseHandle(pi.hThread);

)

return(bCreateOK);

}

//下面的方法創(chuàng)建一個事件和?個子進(jìn)程,然后等待子進(jìn)程在返回前向事件發(fā)出信號

voidWaitForChild()

(

//createaneweventobjectfortheshildprocess

//tousewhenreleasingthisprocess

HANDLEhEvantOcntinu良=::CrAatAEvAnh(

NULL,//缺省的安全性,子進(jìn)程將具有訪問權(quán)限

TRUE,//手工重置事件

FALSE,//初始時是非接受信號狀態(tài)

g_szContir.ueEvent);//事件名稱

if(hEventContinue!=NULL)

(

std::cout<<"eventcreated*'<<std::endl;

//創(chuàng)建子進(jìn)程

if(::CreateChild())

(

std::cout<<"childcreated"<<std::endl;

//等待,直到子進(jìn)程發(fā)出信號

std::cout?nParentwaitingonchild.n?stci::endl;

::WaitForSingleObject(hEventContinue,INFINITE);

::Sleep(1500);//刪去這句試試

std::cout<<Mparentreceivedtheenventsignalingfrom

childn<<std::endl;

}

//清除句柄

::CloseHardle(hEventContinue);

hEventContinue=INVALID_HANDLE_VALUE;

)

)

//以下方法在子進(jìn)程模式下被調(diào)用,其功能只是向父進(jìn)程發(fā)出終止信號

voidSignalparent()

(

//嘗試打開句柄

std::cout<<nchildprocessbeginningu?std::endl;

HANDLEhEventOontinue=::OpenEvent(

EVENT_MODIFY_STATE,//所要求的最小訪問權(quán)限

FALSE,//不是可繼承的句柄

g_szContirueEvent);//事件名稱

if(hEventContinue!=NULL)

90操作系統(tǒng)原理實(shí)驗(yàn)(第二版)

(

::SetEvent(hEventContinue);

std::cout<<'*eventsignaled"<<std::endl;

}

//清除句柄

::CloseHandle(hEventContinue);

hEventContinue=INVALID_HANDLE_VALUE;

}

intmain(intargc,char*argv(])

(

//檢查父進(jìn)程或是子進(jìn)程是否啟動

if(argc>l&&::strcmp(argv[l],Mchild")==0)

(

//向父進(jìn)程創(chuàng)建的事件發(fā)出信號

::SignalParent();

}

else

(

//創(chuàng)建一個事件并等待子進(jìn)程發(fā)出信號

::WaitForChild();

::Sleep(1500);

std::cout<<"Parentreleased."<<std::endi;

}

return0;

)

步驟4:單擊Build菜單中的Compile4-l.cpp命令,并單擊“是”按鈕確認(rèn)。系

統(tǒng)對4-1.cpp進(jìn)行編譯。

步驟5:編譯完成后,單擊Build菜單中的Build4-l.exe命令,建立4-l.exe可執(zhí)

行文件。

操作能否正常進(jìn)行?如果不行,則可能的原因是什么?

步驟6:在工具欄單擊ExecuteProgram(執(zhí)行程序)按鈕,執(zhí)行4-l.exe程序。

請記錄:運(yùn)行結(jié)果(分行書寫。如果運(yùn)行不成功,則可能的原因是什么?):

1)_____________________________________________________________________

2)____________________________________________________________________

3)____________________________________________________________________

4)____________________________________________________________________

5)____________________________________________________________________

6)____________________________________________________________________

請從進(jìn)程并發(fā)的角度對結(jié)果進(jìn)行分析,這個結(jié)果與你期望的一致嗎?

閱讀和分析程序4-1,請回答:

1)程序中,創(chuàng)建一個事件使用了哪一個系統(tǒng)函數(shù)?創(chuàng)建時設(shè)置的初始信號狀態(tài)

是什么?

a.____________________________________________________________________

b.____________________________________________________________________

2)創(chuàng)建一個進(jìn)程(子進(jìn)程)使用了哪一個系統(tǒng)函數(shù)?

3)從步驟6的輸出結(jié)果,對照分析4-1程序,可以看出程序運(yùn)行的流程嗎?請

簡單描述:

4)這是一個簡單的進(jìn)程同步的例子。請簡述進(jìn)程同步在這個程序中是如何實(shí)現(xiàn)

的。

(2)互斥體對象

清單4-2的程序中顯示的類CCountUpDown使用了一個互斥體來保證對兩個線

程間單一數(shù)值的訪問。每個線程都企圖獲得控制權(quán)來改變該數(shù)值,然后將該數(shù)值寫

入輸出流中。創(chuàng)建者實(shí)際上創(chuàng)建的是互斥體對象,計數(shù)方法執(zhí)行等待并釋放,為的

是共同使用互斥體所需的資源(因而也就是共享資源)。

步驟1:編輯實(shí)驗(yàn)源程序4-2.cpp(也可直接打開下載的源程序文件4-2.cpp)o

92操作系統(tǒng)原理實(shí)驗(yàn)(第二版)

清單4-2利用互斥體保護(hù)共享資源

//mutex項(xiàng)目

#include<windows.h>

#include<iostream>

//利用互斥體來保護(hù)司時訪問的共享資源

classCCountUpDown

(

public:

//創(chuàng)建者創(chuàng)建兩個線程來訪問共享值

CCountUpDown(intnAccesses):

m_hThreadInc(INVALID_HANDLE_VALUE),

m_hThreadDec(INVALID_HANDLE_VALUE),

m_hMutexValue(INVALID_HANDLE_VALUE),

mnValue(C),

m_nAccess(nAccesses)

(

//創(chuàng)電互斥體用于訪問數(shù)值

m_hMutexValue=::CreateMutex(

NULL,//缺省的安全性

TRUE,//初始時擁有,在所有的初始化結(jié)束時將釋放

NULL);//匿名的

m_hThreadInc=::CreateThread(

NULL,//缺省的安全性

0,//缺省堆棧

IncThreadProc.//類線程進(jìn)程

reinterpret_cast<LPVOID>(this),//線程參數(shù)

0,//無特殊的標(biāo)志

NULL);//忽略返回的id

m_hThreadDec=::CreateThread(

NULL,//缺省的安全性

0,//缺省堆棧

DecThreadProc,//類線程進(jìn)程

reinterpret_cast<LPVOID>(this)r//線程參數(shù)

0z//無特殊的標(biāo)志

NULL);//忽略返回的id

//允許另一線程獲得互斥體

::ReleaseMutex(m_hMutexValue);

}

//解除程序釋放對對象的引用

virtual~CCountUpDown()

(

::CloseHardle(m_hThreadInc);

::CloseHardle(m_hThreadDec);

::CloseHardle(m_hMutexValue);

}

//簡單的等待方法,在兩個線程終止之前可暫停主調(diào)者

virtualvoidWaitForCompletion()

(

//確保所有對象都已準(zhǔn)備好

if(m_hThreadInc!=INVALID_HANDLE_VALUE&&

m_hThreadDec!=INVALID_HANDLE_VALUE)

(

//等待兩者完成(順序并不重要)

::WaitForSingleObject(m_hThreadInczINFINITE);

::WaitForSingleObject(m_hThreadDeczINFINITE);

)

}

protected:

//改變共享資源妁簡單的方法

virtualvoidDoCount(intnStep)

(

//循環(huán),直到所有的訪問都結(jié)束為止

while(1)

(

//等待訪問數(shù)值

::WaitForSingleObject(m_hMutexValue,INFINITE);

94操作系統(tǒng)原理實(shí)驗(yàn)(第二版)

if(m_nAccess>0)(

//改變并顯示該值

m_nValue+=nStep;

std::cout?''thread:"<<■::GetCurrentThreadld()

〈〈''value:z,?m_nvalue?''access:,z<<m_nAccess

:rendl;

//發(fā)出訪問信號并允許線程切換

——m_nAccess;

::Sleep(1000);//實(shí)現(xiàn)線程切換,并使顯示速度放慢

//釋放對數(shù)值的訪問

::ReleaseMutex(mhMutexValue);

)

elsebreak;

}

)

staticDWORDWINAPIIncThreadProc(LPVOIDIpParam)

(

//將參數(shù)解釋為"this,指針

CCountUpDc-wn*pThis=

reinterpret_cast<CCountUpDown*>(ipParam);

//調(diào)用對象的增加方法并返回一個值

pThis->DoCount(+1);

return(0);

}

staticDWORDWINAPIDecThreadProc(LPVOIDIpParam)

(

//將參數(shù)解釋為'this,指針

CCountUpDown*pThis=

reinterpretcast<CCountUpDown*>(IpParam);

//調(diào)用對象的減少方法并返回?個值

pThis->DOCount(-1);

return(0);

}

protected:

HANDLEm_hThreadInc;

HANDLEm_hThreadDec;

HANDLEm_hMutexValue;

intm_nValue;

intm_nAccess;

};

voidmain()

(

CCountUpDownud(50);

ud.WaitForCompletion();

)

步驟2:單擊Build菜單中的Compile42cpp命令,并單擊“是”按鈕確認(rèn)。系

統(tǒng)對4-2.cpp進(jìn)行編譯。

步驟3:編譯完成后,單擊Build菜單中的Build4-2.exe命令,建立4-2.exe可執(zhí)

行文件。

請記錄:操作能否正常進(jìn)行?如果不行,則可能的原因是什么?

步驟4:在工具欄單擊ExecuteProgram按鈕,執(zhí)行4-2.exe程序。

分析程序4-2的運(yùn)行結(jié)果,可以看到線程(加和減線程)的交替執(zhí)行(因?yàn)镾leep。

API允許Windows切換線程)。在每次運(yùn)行之后,數(shù)值應(yīng)該返回初始值(0),因?yàn)?/p>

在每次運(yùn)行至sleep。語句后,寫入線程在等待隊列中變成最后一個,內(nèi)核保證它在

其他線程工作時不會再運(yùn)行,力口、減線程各自運(yùn)行了25次。

I)請描述運(yùn)行結(jié)果(如果運(yùn)行不成功,則可能的原因是什么?):

96操作系統(tǒng)原理實(shí)驗(yàn)(第二版)

2)根據(jù)運(yùn)行輸出結(jié)果,對照分析4-2程序,可以看出程序運(yùn)行的流程嗎?請簡

單描述加、減線程是如何交替執(zhí)行的?

3)程序4-2中臨界資源、臨界區(qū)各是什么?是加何實(shí)現(xiàn)線程對臨界資源的互斥

訪問的?

3.實(shí)驗(yàn)總結(jié)

4.實(shí)驗(yàn)評價(教師)

4.1.3閱讀與思考

4.2WindowsXP線程間的通訊

WindowsXP提供的線程間通訊類內(nèi)核對象允許同一進(jìn)程或跨進(jìn)程的線程之間互

相發(fā)送信息,包括文件、文件映射、郵件位和命名管道等,其中最常用的是文件和

文件映射。這類對象允許一個線程很容易地向同一進(jìn)程或其他進(jìn)程中的另一線程發(fā)

送信息。

4.2.1文件對象

文件對象是人們所熟悉的永久存儲的傳統(tǒng)元素。將一個文件看作是內(nèi)核對象可

使開發(fā)人員獲得比標(biāo)準(zhǔn)C++文件操作更為強(qiáng)大的功能,

內(nèi)核允許開發(fā)人員在系統(tǒng)設(shè)備或網(wǎng)絡(luò)上創(chuàng)建代表永久存儲數(shù)據(jù)塊的文件對象。

這些文件對象是對永久存儲數(shù)據(jù)的低級訪問者;用C++運(yùn)行庫或其他方法打開的所

有文件最終都要變成對CreateFile()API的調(diào)用。

CreateFile()函數(shù)分配一個內(nèi)核對象來代表一個永久的文件。當(dāng)在磁盤上創(chuàng)建一

個新文件或當(dāng)打開一個已經(jīng)存在的文件時,就調(diào)用這個API,其參數(shù)總結(jié)見表4.2。

表4.2CreateFiie()API的參數(shù)

參數(shù)名使用目的

LPCTSTRIpFilcName要打開或創(chuàng)建的文件名

所要求的文件訪問權(quán);一個包括GENERIC_READ或

DWORDdwDesiredAccess

GENERIC.WRITE的屏蔽

DWORDdwShareMode指定與其他進(jìn)程共享的文件類型(如果有的話)

LPSECURITY_ATTRIBUTES

當(dāng)被文件系統(tǒng)支持時與備份文件對象有關(guān)的安全性

IpSccurityAitributcs

在文件系統(tǒng)的級別上所采取的操作的類型。如新文件的創(chuàng)建

DWORDdwCrealionDisposilion

或打開一個已有的文件

文件系統(tǒng)的屬性,如只讀、隱藏等。還可以是文件對象的屬

DWORDdwFlagsAndAuribiiles

性,如可緩存寫入等

HANDLEhTemplateFile指向另一文件對象的句柄,常用于為新創(chuàng)建的文件提供屬性

創(chuàng)建調(diào)用比創(chuàng)建事件、互斥體或信號量要復(fù)雜。首先必須在IpFilename中指定

對象名,并且要指向文件系統(tǒng)中所訪問的位置。接著必須用dwDesiredAccess參數(shù)提

供所需的訪問級別。

山創(chuàng)建函數(shù)要求的共享模式參數(shù)dwShareMode可以指定當(dāng)另一進(jìn)程企圖同時訪

問數(shù)據(jù)時會發(fā)生什么。與所有其他第一級內(nèi)核對象一樣,可以利用1pSecurityAttributes

參數(shù)指定所創(chuàng)建對象的安全性。接著,要通過dwCrealionDisposilion參數(shù)告訴創(chuàng)建函

數(shù),加果數(shù)據(jù)在指定的永久存儲介質(zhì)中存在或不存在時的行為。

可以使用dwFlagsAndAllribules參數(shù)來指定文件的屬性(如只讀),并確定對數(shù)

據(jù)所執(zhí)行的讀寫操作的行為。最后一個參數(shù)hTemplateFile可指定另一個文件對象作

98操作系統(tǒng)原理實(shí)驗(yàn)(第二版)

為模板,以便為新創(chuàng)建的文件復(fù)制屬性或擴(kuò)展屬性。

WindowsXP系統(tǒng)中包括許多文件對象的工具函數(shù),表4.3中總結(jié)了處理文件對

象時需要使用的API。

表4.3文件對象API

API名稱功能描述

創(chuàng)建文件內(nèi)核對象,用于代表文件系統(tǒng)中新的或已經(jīng)存在的大

CreateFile()

量數(shù)據(jù)

從文件系統(tǒng)中的由文件對象句柄引用的文件發(fā)送數(shù)據(jù)。讀操作

ReadFileO

開始于當(dāng)前文件的指針位置,每讀取一個字節(jié),該位置增加

從文件系統(tǒng)中的由文件對象句柄引用的文件發(fā)送數(shù)據(jù)。寫操作

WriteFileO

開始于當(dāng)前文件的指針位置,每寫入一個字節(jié),該位置增加

SetFilePointer()將文件中的當(dāng)前文件指針位置移動一個相對或絕對距離

SetEndOfFileO將文件的終止記號移動到當(dāng)前文件指針的位置

LockFileO防止其他進(jìn)程訪問傳遞的文件內(nèi)的一個區(qū)域

GetFileType()決定傳遞的句柄是否引用磁盤文件、控制臺或命名的管道

GetFileSizeEx()提取64位的文件容量

GetFileTimeO提取文件創(chuàng)建、最后訪問和最近修改的時間

用傳遞來的文件中的詳細(xì)信息填充BY_HANDLE_FILE_

GetFilelnformationByHandleO

INFORMATION數(shù)據(jù)結(jié)構(gòu)

通常可以使用ReadFile。和WrileFile。API在永久存儲和應(yīng)用程序間通過文件對

象來移動數(shù)據(jù)。因?yàn)閯?chuàng)建調(diào)用將對象的大多數(shù)復(fù)雜性封裝起來了,這兩個函數(shù)只是

簡單地利用指向要交換數(shù)據(jù)的文件對象的句柄(即指向內(nèi)存內(nèi)的數(shù)據(jù)緩存區(qū)的指

針),然后計數(shù)移動數(shù)據(jù)的字節(jié)數(shù)。除此之外,這兩個函數(shù)還執(zhí)行重疊式的輸入和輸

出,由于不會“堵塞”主線程,可用來傳送大量的數(shù)據(jù)。

CreateFile。方法除了可訪問標(biāo)準(zhǔn)的永久文件外,還可訪問控制臺輸入和輸出,

以及從命名的管道來的數(shù)據(jù)。

GelFileType()API指明要處理的關(guān)鍵文件句柄的結(jié)構(gòu)。除此之外,內(nèi)核還提供了

GetFileInformationByHandle()和GetFileSize。>GetFileTime()API用于獲得關(guān)鍵數(shù)據(jù)

的詳細(xì)情況。其他用于在文件中改變數(shù)據(jù)的工具函數(shù)包括LockFileO.SetFilePointerO

和SetEndOfFileOAPL

除了這些基于句柄的API之外,內(nèi)核還提供了大量的工具,用于按文件名對文

件直接操作。文件對象用完之后,應(yīng)該用CloseHandle()API加以清除。

4.2.2文件映射對象

比使用RcadFile。和WritcFilc。API通過文件對象來讀取和寫入數(shù)據(jù)更為簡單的

是,WindowsXP還提供了一種在文件中處理數(shù)據(jù)的方法,名為內(nèi)存映射文件,也稱

為文件映射。文件映射對象是在虛擬內(nèi)存中分配的永久或臨時文件對象區(qū)域(如果可

能的話,可大到整個文件),可將其看作是二進(jìn)制的數(shù)據(jù)塊。使用這類對象,可獲得

直接在內(nèi)存中訪問文件內(nèi)容的能力。

文件映射對象提供了強(qiáng)大的掃描文件中數(shù)據(jù)的能力,而不必移動文件指針。對

于多線程的讀寫操作來說,這一點(diǎn)特別有用,因?yàn)槊總€線程都可能想要把讀取指針

移動到不同的位置去一一為了防止這種情況,就需要使用某種線程同步機(jī)制保護(hù)文

件。

在CreateFileMapping()API中,一個新的文件映射對象需要有一個永久的文件對

象(由CreateFileO所創(chuàng)建)。該函數(shù)使用標(biāo)準(zhǔn)的安全性和命名參數(shù),還有用于允許

操作(如只讀)的保護(hù)標(biāo)忐以及映射的最大容量。隨后可根據(jù)來自O(shè)penFileM叩ping()

API的其他線程或進(jìn)程使用該映射一一這與事件和互斥體的打開進(jìn)程是非常類似的。

內(nèi)存映射文件對象的另一個強(qiáng)大的應(yīng)用是可請求系統(tǒng)創(chuàng)建一個運(yùn)行映射的臨時

文件。該臨時文件提供一個臨時的區(qū)域,用于線程或進(jìn)程互相發(fā)送大量數(shù)據(jù),而不

必創(chuàng)建或保護(hù)磁盤上的文件。利用向創(chuàng)建函數(shù)中發(fā)送INVAL1D_HANDLE_VALUE

來代替真正的文件句柄,就可創(chuàng)建這一臨時的內(nèi)存映射文件;指令內(nèi)核使用系統(tǒng)天

式文件來建立支持映射的最大容量的臨時數(shù)據(jù)區(qū)。

為了利用文件映射對象,進(jìn)程必須將對文件的查看映射到它的內(nèi)存空間中。也

就是說,應(yīng)該將文件映射對象想象為進(jìn)程的第一步,在這一步中,當(dāng)查看實(shí)際上允

許訪問的數(shù)據(jù)時,附加有共享數(shù)據(jù)的安全性和命名方式.為了獲得指向內(nèi)存區(qū)域的

指針需要調(diào)用MapViewOfFileOAPI,此調(diào)用使用文件映射對象的句柄作為其主要參

數(shù)。此外還有所需的訪問等級(如讀-寫)和開始查看時文件內(nèi)的偏移和要查看的容

量。該函數(shù)返回一-個指向進(jìn)程內(nèi)的內(nèi)存的指針,此指針可有多種編程方面的應(yīng)用(但

不能超過訪問權(quán)限)。

當(dāng)結(jié)束文件映射查看時,必須用接受到的指針調(diào)用UnmapVicwOfFlic()AP【,然

后再根據(jù)映射對象調(diào)用CloseHandleOAPL從而將其清除。

4.2.3練習(xí)與實(shí)驗(yàn)

在本實(shí)驗(yàn)中,通過對乂件和文件映射對象的了解,來加深對WindowsXP線程同

I(X)操作系統(tǒng)原理實(shí)驗(yàn)(第二版)

步的理解。

1)回顧系統(tǒng)進(jìn)程、線程的有關(guān)概念,加深對WindowsXP線程間通訊的理解;

2)了解文件和文件映射對象;

3)通過分析實(shí)驗(yàn)程序,了解線程如何通過文件對象發(fā)送數(shù)據(jù);

4)了解在進(jìn)程中如何使用文件對象;

5)通過分析實(shí)驗(yàn)程序,了解線程如何通過文件映射對象發(fā)送數(shù)據(jù);

6)了解在進(jìn)程中如何使用文件映射對象.

1.工具/準(zhǔn)備工作

在開始本實(shí)驗(yàn)之前,請回顧教科書的相關(guān)內(nèi)容。

需要準(zhǔn)備一臺運(yùn)行WindowsXPProfessional操作系統(tǒng)的計算機(jī),且該計算機(jī)中

需安裝VisualC++6.0專業(yè)版或企業(yè)版。

2.實(shí)驗(yàn)內(nèi)容

(1)文件對象

清單4-3中的代碼展示了線程如何通過文件對象在永久存儲介質(zhì)上互相發(fā)送數(shù)

據(jù)。程序只是激活并啟動了一個線程接著一個線程的創(chuàng)建線程。每個線程從指定的

文件中讀取數(shù)據(jù),并對數(shù)據(jù)進(jìn)行修改,其修改增量是以創(chuàng)建時發(fā)送給它的數(shù)量進(jìn)行

的,然后將新數(shù)值寫回文件。

步驟1:登錄進(jìn)入WindowsXPProfessional?

步驟2;在“開始”菜單中單擊MicrosoftVisualC++6.0命令,進(jìn)入VisualC++

窗口。

步驟3:編輯實(shí)險源程序43cpp(也可直接打開下載的源程序文件4-3.叩p)。

清單4-3演示線程通過文件對象發(fā)送數(shù)據(jù)

//fileobj項(xiàng)目

#include<windows.h>

#include<iostream>

//要使用的文件名

staticLPCTSTRg_szFileName=''w2kdg.Fileobj.file.data.txtz,;

//在數(shù)據(jù)文件中讀取當(dāng)前數(shù)據(jù)的簡單線程時將傳遞來的該數(shù)據(jù)增加,并寫回數(shù)據(jù)文件中

staticDWORDWINAPIThreadProc(LPVOIDIpParam)

//將參數(shù)翻譯為長整數(shù)

LONGnAdd=reinterpret_cast<LONG>(IpParam);

//建立完全的指定文件名(包括路徑信息)

TCHARszFullName[MAX_PATH];

::GetTempPath(MAX_PATH,szFullName);//取得路徑

::strcat(szFullName,g_szFileName);

//打開文件對象

HANDLEhFile=::CreateFile(

szFullName,//文件的完全名稱

GENERIC_READ|GENERIC_WRITEZ//具有所有的訪問權(quán)

FILE_SHARE_READZ//允許其他線程讀取

NULL,//缺省的安全性

OPEN_ALWAYS,//創(chuàng)建或打開文件

FILE_ATTRIBUTE_NORMAL,//普通文件

NULL);//無模板文件

if(hFile!=INVALID_HANDLE_VALUE)

(

//讀取當(dāng)前數(shù)據(jù)

LONGnValue(0);

DWORDdwXier(U);

::ReadFile(

hFile,//要讀取的文件

reinterpret_cast<LPVOID>(SnValue),//緩沖區(qū)

sizeof(nValue),//緩沖區(qū)容量

SdwXfer,//讀取的字節(jié)數(shù)

NULL);//無重疊I/O

//顯示當(dāng)前數(shù)據(jù)

std::cout<<''read:,,<<nValue<<std::endl;

)

102操作系統(tǒng)原理實(shí)驗(yàn)(第二版)

//增加數(shù)值

nValue+=nAdd;

//寫回永久存儲介質(zhì)

::SetFilePointer(hFile,O,NULL,FILE_BEGIN);

::WriteFile(

hFile,//要寫入的文件

reinterpretcast<LPCVOID>(&nValue),//數(shù)據(jù)

sizeof(nValue),//緩沖區(qū)容量

&dwXferz//寫入的字節(jié)數(shù)

NULL);//無重登I/O

if(dwXfer==sizeof(nValue))

std::cout<<''v;rite:"z?nValue<<std::endl;

}

〃清除文件

::CloseHandle(hFile);

hFile=INVALID_HANDLE_VALUE;

)

return(0);

)

voidmain()

//創(chuàng)建100個線程從文件中進(jìn)行讀寫

for(intnTotal=100;nTotal>0;-nTotal)

(

//啟動線程

HANDLEhThread=::CreateThread(

NULL,〃缺省的安全性

0,//缺省的堆棧

ThreadProc,//線程函數(shù)

reinterpret_cast<LPVOID>(1)z//增量

0,//無特殊的創(chuàng)建標(biāo)志

NULL);//忽略線程id

//等待線程完成

::WaitForSingleObject(hThread,INFINITE);

::Sleep(5C0);//放慢顯示速度,方便觀察

//釋放指向線程的句柄

::CloseHar.dle(hThread);

hThread=INVALID_HANDLE_VALUE;

}

}

步驟4:單擊Build菜單中的Compile4-3.cpp命令,并單擊“是”確認(rèn)。系統(tǒng)對

4-3.cpp進(jìn)行編譯。

步驟5:編譯完成后,單擊Build菜單中的Build4-3.exe命令,建立4-3.exe可執(zhí)

行文件。

請記錄:操作能否正常進(jìn)行?如果不行,則可能的原因是什么?

步驟6:在工具欄單擊ExecuteProgram按鈕,執(zhí)行4-3.exe程序。

請記錄:運(yùn)行結(jié)果(如果運(yùn)行不成功,則可能的原因是什么?):

閱讀和分析程序4-3,請回答問題:

1)清單4-3中啟動了多少個單獨(dú)的讀寫線程?

2)使用了哪個系統(tǒng)API函數(shù)來創(chuàng)建線程例程?

3)文件的讀和寫操作分別使用了哪個API函數(shù)?

104操作系統(tǒng)原理實(shí)驗(yàn)(第二版)

每次運(yùn)行進(jìn)程時,都可看到清單4-3中的每個線程從前面的線程中讀取數(shù)據(jù)并將

數(shù)據(jù)增加,文件中的數(shù)值連續(xù)增加。這個示例是很簡單的通訊機(jī)制??蓪⑦@一示仞

用作編寫自己的文件讀/寫代碼的模板。

請注意程序中寫入之前文件指針的重置。重置文件指針是必要的,因?yàn)樵撝羔?/p>

在讀取結(jié)束時將處于前四個字節(jié)之后,同一指針還要用于向文件寫入數(shù)據(jù)。如果函

數(shù)向該處寫入新數(shù)值,則下次進(jìn)程運(yùn)行時,只能讀到原來的數(shù)值。那么:

4)在程序中,重置文件指針使用了哪一個函數(shù)?

5)從步驟6的輸出結(jié)果,對照分析4-3程序,可以看出程序運(yùn)行的流程嗎?請

簡單描述:

6)程序main函數(shù)中,語句::WaitForSingleObject(hThread,INFINITE);有何作

用?

(2)文件映射對象

清單4-4的程序展示了一個在線程間使用的由頁式文件支持的文件映射對象,從

中可以看出利用內(nèi)存映射文件比使用駐留在磁盤上的文件對象更為簡單。其中的正

程還使用了互斥體,以便公平地訪問文件映射對象,然后,當(dāng)每個線程都釋放時,

程序?qū)⑽募囊晥D映射到文件上并增加數(shù)據(jù)的值。

步驟1:編輯實(shí)驗(yàn)源程序4-4.cpp(也可直接打開下我的源程序文件4-4.cpp)o

清單4-4演示使用映射文件的內(nèi)存交換數(shù)據(jù)的線程

//mappings項(xiàng)目

#include<windows.h>

#include<iostream>

//仲裁訪問的互斥體

staticHANDLEg_hMutexMapping=INVALID_HANDLE_VALUE;

//增加共享內(nèi)存中的數(shù)值的簡單線程

staticDWORDWINAPIThreadProc(LPVOIDIpParam)

//將參數(shù)看作句柄

HANDLEhMapping=reinterpret_cast〈HANDLE〉(IpParam);

//等待對文件的訪問

::WaitForSingleObject(g_hMutexMapping,INFINITE);

〃映射視圖

LPVOIDpFile=::MapViewOfFile(

hMapping,//保存文件的對象

FILE_MAP_ALLACCESS,//獲得讀寫權(quán)限

0,//在文件的開頭處(高32位)開始

0,//...(低32位)

0);//映射整個文件

if(pFile!=NULL)

(

//將數(shù)據(jù)看作長整數(shù)

LONG*pnData=reinterpret__cast<LCNG*>(pFile);

//改動數(shù)據(jù)

++(*pnData);

//顯示新數(shù)值

std::cout<<''thread:''<<::GetCurrentThreadld()

<<"value:''<<(*pnData)<<std::endl;

//釋放文件視圖

::UnmapViGwOfFile(pFile);

pFile=NULL;

}

//釋放對文件的訪問權(quán)

::AasAMut.Ax(g_hMut.AxMapping);

return(0);

|

106操作系統(tǒng)原理實(shí)驗(yàn)(第二版)

//創(chuàng)建共享數(shù)據(jù)空間

HANDLEMakeSharedFile()

//創(chuàng)建文件映射對象

HANDLEhMapping=::CreateFileMapping(

INVALID_HANDLE_VALUE,//使用頁式文件臨時文件

NULL,//缺省的安全性

PAGEREADWRITE,//可讀寫權(quán)

Oz//最大容量(高32位)

sizeof(LONG),//...(低32位)

NULL);//匿名的

if(hMapping!=INVALID_HANDLE_VALUE)

//在文件映對上創(chuàng)建視圖

LPVOIDpDsta=::MapViewOfFile(

hMapping,//保存文件的對象

FILE_MAP_ALL_ACCESS,//獲得讀寫權(quán)

0,//在文伯的開頭處(高32位)開始

...(低位)

0z//32

0);//映射整個文件

if(pData!=NULL)

::ZeroMemory(pData,sizeot(LONG));

)

//關(guān)閉文件視圖

::UnmapViewOfFile(pData);

}

return(hMapping);

}

voidmain()

//創(chuàng)建數(shù)據(jù)文件

HANDLEhMapping=::MakeSharedFile();

//創(chuàng)建仲裁的互斥體

g_hMutexMapping=::CreateMutex(NULL,FALSE,NULL);

//根據(jù)文件創(chuàng)建100個線程來讀寫

for(intnTotal=100;nTotal>0;--nTotal)

(

//啟動線程

HANDLEhThread=::CreateThread(

NULL,//缺省的安全性

0,//缺省堆棧

ThreadProc,//線程函數(shù)

reinterpret_cast<LPVOID>(hMapping),//增量

0,//無特殊的創(chuàng)建標(biāo)志

NULL);//忽略線程id

//等待最后的線程釋放

if(nTotal==1)

(

std::cout?''allt

溫馨提示

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

最新文檔

評論

0/150

提交評論