同步設(shè)備IO
所謂同步IO是指線程在發(fā)起IO請(qǐng)求后會(huì)被掛起,,IO完成后繼續(xù)執(zhí)行。
異步IO是指:線程發(fā)起IO請(qǐng)求后并不會(huì)掛起而是繼續(xù)執(zhí)行,。IO完畢后會(huì)得到設(shè)備的通知,。而IO完成端口就是實(shí)現(xiàn)這種通知的很好的一種方式。
線程是我們開發(fā)高性能,、響應(yīng)性好的一個(gè)必不可少的工具,。這樣在多處理器上就可以同時(shí)執(zhí)行多個(gè)操作,,從而提高吞吐量,。當(dāng)線程發(fā)出一個(gè)同步設(shè)備IO請(qǐng)求的時(shí)候,它會(huì)被臨時(shí)掛起,,直到設(shè)備完成IO請(qǐng)求為止,。但線程阻塞會(huì)損害性能,,這里有個(gè)問題是我們?nèi)绾巫尵€程不被掛起。
讓線程始終進(jìn)行有用的工作就需要它們相互通信,,鼎力配合,。Windows經(jīng)過數(shù)年的研究和測(cè)試,開發(fā)出了一種被稱為IO完成端口的機(jī)制的技術(shù),。它可以幫助我們創(chuàng)建高性能而且伸縮性好的應(yīng)用程序,。通過使用完成端口我們可以讓線程在讀取設(shè)備和寫入設(shè)備而不必等待設(shè)備的響應(yīng),從而顯著的提高吞吐量,。
作為Windows程序員都必須要完全理解IO完成端口的工作原理,。
Windows支持多種不同種類的設(shè)備。在此,,我們把設(shè)備定義為能夠與之進(jìn)行通信的任何東西,。如文件、目錄,、串口,、并口、套接字,、控制臺(tái)等,。接下來討論是與這些設(shè)備進(jìn)行通信,此種方式下與線程通信時(shí),,線程需要掛起等待設(shè)備響應(yīng)---這種方式被稱為同步IO,。
Windows向開發(fā)人員隱藏了各種設(shè)備的差異,許多WindowsAPI允許我們以相同的方式來從設(shè)備讀取數(shù)據(jù)和向設(shè)備寫入數(shù)據(jù),,而不必關(guān)心何種類型的設(shè)備,。
CreateFile函數(shù)。
CreateFile當(dāng)然可以創(chuàng)建和打開磁盤文件,。但是不要被它的名字所迷惑,。它同樣可以打開其他設(shè)備。根據(jù)傳入?yún)?shù)的不同可以讓CreateFile打開不同的設(shè)備,。
- HANDLE CreateFile(
-
- PCTSTR pszName,
-
- DWORD dwDesiredAccess,
-
- DWORD dwShareMode,
-
- PSECURITY_ATTRIBUTES psa,
-
- DWORD dwCreationDisposition,
-
- DWORD dwFlagsAndAttributes,
-
- HANDLE hFileTemplate);
psaName既表示設(shè)備類型也表示該類設(shè)備一個(gè)實(shí)例,。
dwDesiredAccess用來指定我們以何種方式和設(shè)備通信??梢詡魅胍韵轮担?/span>
0 不允許讀寫,,但可以改變?cè)O(shè)備屬性。
GENERIC_READ 只讀訪問
GENERIC_WRITE 只寫訪問
GENERIC_READ|GENERIC_WRITE 讀寫訪問,。
dwSharedMode用來指定共享權(quán)限:
0 獨(dú)占對(duì)設(shè)備的訪問,。如果設(shè)備已經(jīng)打開,我們 的CreateFile會(huì)失敗。
FILE_SHARE_READ 只讀共享,,不允許修改內(nèi)容,。如果設(shè)備已經(jīng)以寫入或獨(dú)占方式打開,我們的CreateFile會(huì)失敗,。
FILE_SHARE_WRITE 寫共享,,不允許讀取內(nèi)容。如果設(shè)備已經(jīng)以讀取或獨(dú)占方式打開,,我們的CreateFile會(huì)失敗,。
FILE_SHARE_READ|FILE_SHARE_WRITE 不關(guān)心向設(shè)備讀還是寫數(shù)據(jù)。如果設(shè)備已經(jīng)以獨(dú)占方式打開,,我們的CreateFile會(huì)失敗,。
FIEL_SHARE_DELETE 先將文件標(biāo)記待刪除,所有對(duì)該文件引用的句柄都關(guān)閉之后,,才將其真正的刪除,。
psa指向一個(gè)PSECURITY_ATTRIBUTES結(jié)構(gòu),用來指定安全屬性,。只有當(dāng)我們?cè)诰邆浒踩缘奈募到y(tǒng)中,,如NTFS中創(chuàng)建文件時(shí)才會(huì)用到此結(jié)構(gòu)。在其他情況下都只需要傳入NULL就可以了,,此時(shí)會(huì)用默認(rèn)的安全屬性來創(chuàng)建文件,,并且返回的句柄是不可繼承的。
dwCreationDisposition參數(shù)對(duì)文件的含義更重大,。它可以是以下值:
CREATE_NEW 創(chuàng)建一個(gè)新文件,。如果同名文件存在則失敗。
CREATE_ALWAYS 文件同名文件存在與否都創(chuàng)建文件,。存在時(shí)會(huì)覆蓋,。
OPEN_EXISTING 打開一個(gè)已存在文件。如不存在,,則失敗,。
OPEN_ALWAYS 打開一個(gè)已存在文件。如不存在,,則創(chuàng)建,。
TRUNCATE_EXISTING 打開一個(gè)已存在文件,將文件大小截?cái)酁?span style="font-family:Times New Roman">0,如果不存在則調(diào)用失敗,。
dwFlagsAndAttributes有兩個(gè)用途:一,,允許我們?cè)O(shè)置一些標(biāo)志微調(diào)與設(shè)備的通信。二:如果設(shè)備是文件,,還可以設(shè)置文件屬性,。這些標(biāo)志大多數(shù)是一些信號(hào),用來告訴系統(tǒng)我們打算以何種方式來訪問設(shè)備,這樣系統(tǒng)就可以對(duì)緩存算法進(jìn)行優(yōu)化,。此處不再介紹。
hFileTemplate,,既可以標(biāo)識(shí)一個(gè)已經(jīng)打開的文件句柄,,也可以是NULL。如果是一個(gè)文件句柄,,那么CreateFile會(huì)完全忽略dwFlagsAndAttributes參數(shù),,轉(zhuǎn)而使用hFileTemplate標(biāo)識(shí)的文件屬性。此時(shí),,hFileTemplate標(biāo)識(shí)的文件句柄必須是一個(gè)用GENERIC_READ標(biāo)志打開的文件,。
CreateFile成功的創(chuàng)建或打開設(shè)備那會(huì)返回設(shè)備句柄。否則返回INVALID_HANDLE_VALUE,。一定要注意返回值不是NULL哦,。
Windows在設(shè)計(jì)時(shí)使用了64位來表示文件大小。但是64位需要分兩個(gè)32位值來傳入,。實(shí)際上在日常工作中還有使用大于4G的文件,。高32位在大多數(shù)情況下都會(huì)是0。
GetFileSizeEx用于得到文件大小,。
- BOOL GetFileSizeEx(
-
- HANDLE hFile,
-
- PLARGE_INTEGER pliFileSize);
-
-
hFile表示一個(gè)一打開文件的句柄,。
pliFileSize表示文件大小。定義如下:
- typedef union _LARGE_INTEGER
-
- {
-
- struct
-
- {
-
- DWORD LowPart;
-
- LONG HighPart;
-
- };
-
- LONGLONG QuadPart;
-
- }LARGE_INTEGER,*PLARGE_INTEGER;
它允許我們以一個(gè)64位有符號(hào)數(shù)或者是兩個(gè)32位值來表示一個(gè)64位數(shù),。
另外一個(gè)很重要的函數(shù)是GetCompressedFileSize:
- DWORD GetCompressedFileSize(
-
- PCTSTR pszFileName,
-
- PDWORD pdwFileSizeHigh);
這個(gè)函數(shù)返回文件物理大小,,而GetFileSizeEx是返回文件邏輯大小。
CreateFile會(huì)創(chuàng)建一個(gè)文件內(nèi)核對(duì)象來管理文件,。返回的句柄就是對(duì)該文件內(nèi)核對(duì)象的引用,。在這個(gè)內(nèi)核對(duì)象中有一個(gè)文件指針,它表示應(yīng)該在哪里執(zhí)行下一次讀取或?qū)懭氩僮?。開始時(shí)它的值是0,。
SetFilePointerEx可以通過操作文件指針實(shí)現(xiàn)隨機(jī)訪問文件。
- BOOL SetFilePointerEx(
-
- HANDLE hFile,
-
- LARGE_INTEGER liDistanceToMove,
-
- PLARGE_INTEGER pliNewFilePointer,
-
- DWORD dwMoveMethod);
hFile表示我們要操作的文件指針,。
liDistanceToMove指定我們要移動(dòng)文件指針的字節(jié)數(shù),。系統(tǒng)會(huì)把我們指定的值與文件指針的當(dāng)前值相加。傳入負(fù)值是合法的,。
dwMoveMethod指定移動(dòng)文件指針的起始位置,。
FILE_BEGIN 從文件開頭開始。
FILE_CURRENT 從當(dāng)前位置開始,。
FILE_END 從文件末尾,。
pliNewFilePointer返回文件指針的新值。
設(shè)置文件尾。
- BOOL SetEndOfFile(HANDLE hFile);
此函數(shù)會(huì)根據(jù)文件對(duì)象當(dāng)前的文件指針當(dāng)前所在的位置,,截?cái)辔募?。如果想要將文件設(shè)置為2k,可以這樣:
- LARGE_INTEGER li;
-
- li.QuadPart=2048;
-
- SetFilePointerEx(hFile,li,NULL,FILE_BEGIN);
-
- SetEndOfFile(hFile);
-
- CloseHandle(hFile);
ReadFile和WriteFile
- BOOL ReadFile(
-
- HANDLE hFile,
-
- PVOID pvBuffer,
-
- DWORD nNumBytesToRead,
-
- PDWORD pdwNumBytes,
-
- OVERLAPPED*pOverlapped);
-
- BOOL WriteFile(
-
- HANDLE hFile,
-
- CONST VOID *pvBuffer,
-
- DWORD nNumBytesToWrite,
-
- PDWORD pdwNumbytes,
-
- OVERLAPPED*pOverlapped);
hFile表示我們要訪問的設(shè)備,。調(diào)用CreateFile打開設(shè)備時(shí)一定不能指定FILE_FLAG_OVERLAPPED標(biāo)志,,否則系統(tǒng)認(rèn)為我們想要與該設(shè)備執(zhí)行異步IO。
pvBuffer指向一個(gè)緩存,,函數(shù)會(huì)把設(shè)備數(shù)據(jù)讀取到該緩存中或者把該緩存的數(shù)據(jù)寫入設(shè)備,。
nNumbytesToRead和nNumBytesToWrite分別告訴ReadFile和WriteFile要從設(shè)備讀取或?qū)懭攵嗌僮止?jié)。
pdwNumBytes返回讀取的字節(jié)或向設(shè)備寫入的字節(jié),。
在執(zhí)行同步IO時(shí),,最后一個(gè)參數(shù)pOverlapped應(yīng)該被設(shè)為NULL。
ReadFile和WriteFile執(zhí)行成功后都返回true,。
FlushBuffers將數(shù)據(jù)刷新至設(shè)備,。
- BOOL FlushFileBuffers(HANDLE hFile);
該函數(shù)會(huì)強(qiáng)制將hFile參數(shù)所標(biāo)識(shí)的設(shè)備相關(guān)聯(lián)的所有緩存寫入設(shè)備。
同步IO很容易使用,,但是它會(huì)阻塞線程,。比如:如果由于CreateFile正在執(zhí)行同步IO操作而導(dǎo)致線程被阻塞,那么該線程的其他操作都會(huì)得不到處理,。更嚴(yán)重的情況是會(huì)導(dǎo)致應(yīng)用程序停止響應(yīng),。Windows允許我們?nèi)∠付ň€程尚未完成的同步IO請(qǐng)求。
- BOOL CancelSynchronousIo(HANDLE hThread);
hThread標(biāo)識(shí)由于等待同步IO完成而被掛起的線程句柄,。這個(gè)句柄必須是使用THREAD_TERMINATE訪問權(quán)限創(chuàng)建的,。否則,函數(shù)會(huì)失敗,。GetLastError會(huì)返回ERROR_ACCESSS_DENIED,。
我們自己創(chuàng)建的線程的安全屬性是THREAD_ACCESS的,其中包括THREAD_TERMINATE,。如果我們利用線程池,,那么我們必須調(diào)用OpenThread來得到當(dāng)前線程標(biāo)識(shí)符對(duì)應(yīng)的線程句柄,同時(shí)傳入THREAD_TERMINATE,。
CanelSynchronousIo會(huì)將等待IO完成而被掛起的線程喚醒,。如果線程并不是因?yàn)橐却O(shè)備響應(yīng)而被掛起,函數(shù)返回false,。GetLastError返回ERROR_NOT_FOUND,。
即便如此,為了創(chuàng)建響應(yīng)性好的應(yīng)用程序我們應(yīng)該盡可能的執(zhí)行異步IO操作,。下一篇博文會(huì)有詳細(xì)介紹,。
本博文參考自《Windows核心編程》第五版 第二部分,。如有紕漏,請(qǐng)不吝賜教,!
|