windows 核心編程之 10 同步裝置IO與非同步裝置IO

來源:互聯網
上載者:User
 寫讀書筆記的目的是加強理解,記錄自己學習的過程 在microsoft Windows 應用程式中,線程是我們最好的工具,可以用來對工作進行劃分。 為了不讓線程閑下來,我們需要讓各個線程就他們正在執行的操作相互連信。有一種非常好的機制來進行這類通訊。 它就叫IO完成連接埠,它可以協助我們建立高效能而且伸縮性好的應用程式。通過使用IO完成連接埠,我們可以讓線程在讀取裝置和寫入裝置的時候不必等待裝置的響應,從而顯著的提高輸送量。
作為一名Windows開發人員,必須完全理解IO完成連接埠的工作方式,IO完成連接埠可以與裝置有關係,也可以與裝置無關,它是一種有無數種用途的絕佳的線程間通訊機制。
1  開啟和關閉裝置 Windows 的優勢之一是它支援的裝置數量,就是我們與之能夠通訊的任何東西.

用來開啟各種裝置的函數如下:


一般的裝置控制代碼使用closehandle來關閉,通訊端使用colosesocket.
GetFileType來查詢裝置的類型,函數返回下列值.



細看CrateFile函數 HANDLE
CreateFile( 
 LPCTSTRlpFileName, 檔案名稱  DWORDdwDesiredAccess, 訪問模式  DWORDdwShareMode,  共用模式  LPSECURITY_ATTRIBUTESlpSecurityAttributes, 安全屬性  DWORDdwCreationDisposition, 建立標誌  DWORDdwFlagsAndAttributes,  讀取標誌位和檔案屬性  非常大的檔案這個標誌位 FILE_FLAG_NO_BUFFERING  HANDLEhTemplateFile); 模板檔案

函數成功返迴文件控制代碼,失敗時返回invalid_handle_value Handle  hFile = CreateFile(...); if( invalid_handle_value == hFile) { 開啟檔案控制代碼失敗. } else { 開啟成功. } 2 使用檔案裝置 GetfileSize(handle,pDWORD)  獲得檔案大小,當檔案大於4GB時,Pword參數表示高位,傳回值表上地位.  GetCompressedFileSize 返回物理檔案大小

設定檔案指標的位置
BOOL
SetFilePointerEx( 
 HANDLEhFile,  檔案控制代碼對象  LARGE_INTEGERliDistanceToMove, 需要移動的位元組數  PLARGE_INTEGERlpNewFilePointer, 更新後的檔案指標位置  DWORDdwMoveMethod); FILE_BEGIN ,FILE_CURRENT , FILE_END

設定檔案尾部 強制檔案變小或者變大DWORD dSize,dsize1;
LARGE_INTEGER LagreSize;
LagreSize.QuadPart = 1024;
SetFilePointerEx(hFile, LagreSize, NULL, FILE_BEGIN);
SetEndOfFile(hFile);
CloseHandle(hFile); 3 執行同步裝置IO
最常用的對裝置資料的讀取的函數是Readfile,WriteFile函數 將資料重新整理至裝置 FlushFileBuffers(HANDLE hfile);//把所以緩衝區的資料重新整理到指定檔案控制代碼
同步IO比如,Createfile函數會阻塞其他調用線程。必須等待函數返回才可以繼續執行.
在WindowsVista中 CancelSynchronousIo(HANDLE hThread); 取消指定線程沒有完成的同步IO請求 hThread 線程控制代碼必須要有 THREAD_TERMINATE許可權
返回非0表示函數成功,返回0表示失敗,更多資訊見MSDN
4 非同步裝置IO基礎 把非同步IO請求排入佇列是開發高效能和可伸縮高的應用程式的本質所在。
為了以非同步方式訪問裝置,在CreatFile時 dwFlagsAndAttributes標示必須指定FILE_FLAG_OVERLAPPED 非同步重疊IO。 表示我們想要用非同步方式來訪問裝置。

OVERLAPPED 結構 The OVERLAPPED structure contains information used in asynchronous (or overlapped) input and output (I/O). 用於非同步通訊的一個結構體資訊.
typedef struct _OVERLAPPED {  ULONG_PTR Internal;   由驅動程式設定 這個成員用來儲存已處理的IO請求的錯誤碼  ULONG_PTR InternalHigh;
由驅動程式設定 表示IO完成時已經傳輸的位元組數

union { struct { DWORD Offset; 如果裝置是檔案時需要設定讀取位移量,低32位 如果不是檔案時這2個參數都設定為0 DWORD OffsetHigh;

如果裝置是檔案時需要設定讀取位移量,高32位 大於4GB檔案需要設定

}; PVOID Pointer; 系統保留 }; HANDLE hEvent; 事件對象,當Io完成時,它變成有訊號狀態

} OVERLAPPED, *LPOVERLAPPED;
HasOverlappedIoCompleted宏使用的就是第一個參數, 函數返回true表示IO請求完成,false表示IO請求沒有完成.
非同步裝置IO的注意事項1. 非同步IO的請求不是先進先出,也就是說裝置驅動根據效率自己選擇執行哪一個IO請求。1ReadFile 

2WriteFile 先調用讀檔案,在調用寫檔案,他們都是非同步請求的,那麼有可能先寫檔案,在讀檔案。

2. 如何正確的來檢查錯誤 如果請求的IO非同步方式執行,那麼Readfile或者WriteFile返回 false,那麼需要調用GetLastError 來獲得資訊,如果返回的是 ERROR_IO_PENDING 表示IO請求已經排入佇列,晚些時候完成.

3. 非同步IO完成前,一定不能銷毀,或者移動資料緩衝和overlapped結構體資訊.

必須為每個IO請求初始化和分配一個不同的overlapped結構體資訊void ReadData(handle hfile){ overlapped ol = {0};byte b[100]; 
reatefile(hfile, b, 100, NULL, &ol);}看上去這段代碼沒有問題,但其實有很大隱患。非同步IO請求排入佇列以後,函數返回了,局部變數釋放。處理IO請求的驅動程式並不知道,它會去修改那個地址,有可能引起崩潰。如果裝置驅動以同步方式處理可能沒有問題,但是以非同步方式處理時,就修改了記憶體內容,並且很難發現。取消列隊中的裝置IO請求1 CancelIO(handle hFile)2 關閉裝置控制代碼來取消所有添加到列表的請求,不管是哪個線程添加的3 線程終止時,系統自動取消該線程添加的所有IO請求。4 給定檔案控制代碼的一個指定的IO請求關閉時,調用CancleioEx(HANDLE hfile, LPOVERLAPPED lOverlapped);如果loverlapped不為NULL那麼取消一個特定的IO請求,如果為NULL取消所有控制代碼為HFile的IO請求。5 接收IO請求完成通知Windows 提供了4種不同的方法來通知IO請求已經完成的通知. 下列表格表示從容易到困難的IO完成連接埠無疑是最好的。1 觸發裝置核心對象 沒有多大用處,只能處理一個IO請求代碼如下:
//非同步方式開啟一個檔案控制代碼HANDLE hFile = CreateFile(L"sn_new.txt",GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ,NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED,NULL);if (INVALID_HANDLE_VALUE == hFile){printf("file open faild\n");return 0;}OVERLAPPED overlapped = {0};overlapped.Offset = 100; //表示從100的位置開始讀取 大於4GB檔案才需要設定 OffsetHighbyte buffer[100]= {0};bool bReadDone = ReadFile(hFile, buffer, 100, NULL, &overlapped);if (!bReadDone && ERROR_IO_PENDING == GetLastError()){//表示IO請求已經排入佇列,稍候完成bool bStop = false;while(!bStop){//表示裝置核心觸發了DWORD dRes = WaitForSingleObject(hFile, 1000);//等待1秒switch(dRes){case WAIT_OBJECT_0://表示IO處理完畢,buffer有資料了bStop = true;break;case WAIT_TIMEOUT://表示IO沒有處理完畢break;default: WAIT_FAILED:break;}Sleep(2000);//休息2秒}}else{//表示讀取檔案錯誤,更多資訊見GetLastError();}

2 觸發事件核心對象 比裝置核心對象好用,可以處理多個IO請求下面的代碼是核心對象處理IO請求的局限性

//非同步方式開啟一個檔案控制代碼HANDLE hFile = CreateFile(L"sn_new.txt",GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ,NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED,NULL);OVERLAPPED Readoverlapped = {0};Readoverlapped.Offset = 100; //表示從100的位置開始讀取 大於4GB檔案才需要設定 OffsetHighbyte buffer[100]= {0};bool bReadDone = ReadFile(hFile, buffer, 100, NULL, &Readoverlapped);OVERLAPPED WriteOverlapped = {0};WriteOverlapped.Offset = 0; //表示從檔案開始的位置寫byte writeBuffer[100]={"dfffffffffffff"};bool bWirteDone = WriteFile(hFile, writeBuffer,100,NULL,&WriteOverlapped);WaitForSingleObject(hFile, INFINITE);// 如果等待檔案控制代碼有資訊了,那麼是讀完成了,還是寫完成了,無法區別

所以事件核心對象出場了.
 OVERLAPPED 結構的最後一個成員變數 hevent 表示一個事件,利用CreateEvent函數來建立一個事件當IO請求處理完畢時,裝置驅動程式把這個事件設定為有訊號狀態
那麼我們應該只等待事件對象,不在應該等待裝置核心對象了。
關閉觸發檔案對象有一個函數
SetFileCompletionNotificationModes(HANDLE hfile, UCHAR flags = FILE_SKIP_SET_EVENT_ON_HANDLE);
表示這個檔案核心對象有IO完成時,也不會觸發

下面的代碼介紹了怎麼使用事件對象:

//下面使用事件核心對象來處理IO完成請求//非同步方式開啟一個檔案控制代碼HANDLE hFile = CreateFile(L"sn_new.txt",GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ,NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED,NULL);OVERLAPPED Readoverlapped = {0};Readoverlapped.Offset = 100; //表示從100的位置開始讀取 大於4GB檔案才需要設定 OffsetHighReadoverlapped.hEvent = CreateEvent(NULL, FALSE, FALSE,NULL);//預設安全屬性,自動事件,未觸發,匿名byte buffer[100]= {0};bool bReadDone = ReadFile(hFile, buffer, 100, NULL, &Readoverlapped);OVERLAPPED WriteOverlapped = {0};WriteOverlapped.Offset = 0; //表示從檔案開始的位置寫WriteOverlapped.hEvent = CreateEvent(NULL, FALSE, FALSE,NULL);byte writeBuffer[100]={"dfffffffffffff"};//預設安全屬性,自動事件,未觸發,匿名bool bWirteDone = WriteFile(hFile, writeBuffer,100,NULL,&WriteOverlapped);HANDLE hEventArray[2];hEventArray[0] = Readoverlapped.hEvent;hEventArray[1] = WriteOverlapped.hEvent;DWORD drs = WaitForMultipleObjects(_countof(hEventArray), hEventArray, FALSE, 2000);switch(drs){case WAIT_OBJECT_0://讀完成break;case WAIT_OBJECT_0 + 1://寫完成break;case WAIT_TIMEOUT://讀和寫都沒有完成 去泡妞吧break;case WAIT_FAILED://控制代碼無效break;}

檢查重疊IO的結果

BOOL GetOverlappedResult( HANDLE hFile, LPOVERLAPPEDlpOverlapped, LPDWORDlpNumberOfBytesTransferred,
BOOL
bWait);參數1 表示裝置控制代碼參數2 表示 重疊IO結構體參數3 表示 返回的位元組數參數4 表示 是否一直等待直到返回結果為止 3  可提醒IO一般不推薦使用,請注意它的一些基礎設施。允許我們手動添加一項到ACPQueueUserAPC(
    __in PAPCFUNC pfnAPC, 函數指標
    __in HANDLE hThread,  線程ID
    __in ULONG_PTR dwData 回呼函數的值
    );如果線程建立的時候沒有運行,那麼如果有ACP請求,會先處理APC,然後才開始運行線程,並且一起處理所以的APC即使線程掛起,也會把ACP請求添加到ACP隊列中去,如果使用了 可提醒函數等待的話,那麼每次函數執行時都會把所以的ACP列隊中的請求處理完,才返回,傳回值為WAIT_IO_COMPLETION,如果下次又有新的請求,那麼繼續要調用可提醒函數,才能處理列隊中的請求。IO完成連接埠另外發一篇文章單獨詳細說明
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.