多线程笔记5
第六章:Overlapped I/O,在你身后變戲法
1.overlapped I/O 是 Win32 的一項技術,你可以要求操作系統為你傳送數據,并且在傳送完畢時通知你。這項技術使你的程序在I/O 進行過程中仍然能夠繼續處理事務。事實上,操作系統內部正是以線程來完成 overlapped I/O。
2.Win32文件操作函數
(1)CreateFile()可以用來打開文件、串行口和并行口、Named pipes、Console。
HANDLE CreateFile(
LPCTSTR lpFileName, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 指向文件名稱
DWORD dwDesiredAccess, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 存取模式(讀或寫)
DWORD dwShareMode, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 共享模式(share mode)
LPSECURITY_ATTRIBUTES lpSecurityAttributes, ?// 指向安全屬性結構
DWORD dwCreationDisposition, ? ? ? ? ? ? ? ? ? ? ? ? ?// 如何產生
DWORD dwFlagsAndAttributes, ? ? ? ? ? ? ? ? ? ? ? ? ?// 文件屬性
HANDLE hTemplateFile ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 一個臨時文件,將擁有全部的屬性拷貝
);
注:overlapped I/O性質:可以在同一時間讀寫文件的許多部分;多個overlapped請求,執行次序無法保證;overlapped I/O的基本型式是以ReadFile()和WriteFile()完成的。
(2)ReadFile()
BOOL ReadFile(
HANDLE hFile, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 欲讀之文件
LPVOID lpBuffer, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 接收數據之緩沖區
DWORD nNumberOfBytesToRead, ? ? ? ? // 欲讀取的字節個數
LPDWORD lpNumberOfBytesRead, ? ? ? ?// 實際讀取的字節個數的地址
LPOVERLAPPED lpOverlapped ? ? ? ? ? ? ? // 指針,指向 overlapped info
);
(3)WriteFile()
BOOL WriteFile(
HANDLE hFile, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 欲寫之文件
LPCVOID lpBuffer, ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 儲存數據之緩沖區
DWORD nNumberOfBytesToWrite, ? ? ? // 欲寫入的字節個數
LPDWORD lpNumberOfBytesWritten, ? // 實際寫入的字節個數的地址
LPOVERLAPPED lpOverlapped ? ? ? ? ? ? // 指針,指向 overlapped info
);
如果CreateFile() 的第6個參數被指定為FILE_FLAG_ OVERLAPPED,就必須在上述的 lpOverlapped 參數中提供一個指針,指向一個 OVERLAPPED 結構。
(4)OVERLAPPED結構
typedef struct _OVERLAPPED {
DWORD Internal; ? ? ? ?//通常保留。當GetOverlappedResult傳回False,并且GetLastError并非傳回ERROR_IO_PENDING,則內含一個視系統而定的狀態。
DWORD InternalHigh; //通常被保留,當GetOverlappedResult傳回True,則內含“被傳輸數據的長度”
DWORD Offset; ? ? ? ? ?//讀、寫偏移位置,從文件頭開始算起。若目標設備不支持文件位置,忽略
DWORD OffsetHigh; ? //64位文件偏移中較高32位。若目標設備不支持文件位置,忽略
HANDLE hEvent; ? ? ? //manual reset event,overlapped I/O完成時被激發。ReadFileEX,WriteFileEX忽略這個欄位,彼時被用來傳遞一個用戶自定義的指針。
} OVERLAPPED, *LPOVERLAPPED;
OVERLAPPED 結構執行兩個重要的功能。第一,它像一把鑰匙,用以識別每一個目前正在進行的 overlapped 操作。第二,它在你和系統之間提供了一個共享區域,參數可以在該區域中雙向傳遞。
通常overlapped結構存放在heap中。
3.被激發的File Handles
(1)異步IO的步驟:CreateFile指定FILE_FLAG_OVERLAPPED;設立一個OVERLAPPED結構,調用ReadFile、WriteFile帶上這個參數。
(2)文件handle是一個核心對象,一旦操作完畢即被激發。
(3)GetOverlappedResult()
BOOL GetOverlappedResult(
HANDLE hFile, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//文件設備的handle
LPOVERLAPPED lpOverlapped, ? ? ? ? ? ? ? ? //一個指針,指向overlapped結構
LPDWORD lpNumberOfBytesTransferred, ?//一個指針,指向DWORD,保存真正被傳輸的字節數。
BOOL bWait ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//是否要等待操作完成,TRUE表示等待。
);
(4)雖然你要求一個overlapped 操作,但它并不一定就是 overlapped!如果數據已經被放進 cache中,或如果操作系統認為它可以很快速地取得那份數據,那么文件操作就會在ReadFile() 返回之前完成,而 ReadFile() 將傳回 TRUE。
(5)一個文件操作為 overlapped,而操作系統把“操作請求”放到隊列中等待執行, ReadFile() 和、WriteFile()都會傳回 FALSE 以示失敗。這個行為并不是很直觀, 你必須調用GetLastError() 并確定它傳回 ERROR_IO_PENDING,那意味著“overlappedI/O 請求”被放進隊列之中等待執行。GetLastError() 也可能傳回其他的值,例如 ERROR_HANDLE_EOF,那就真正代表一個錯誤了。
4.被激發的event對象
(1)所使用的 event 對象必須是手動重置(manual-reset)而非自動重置(auto-reset)。
(2)IOBYEVENT例子。
5.異步過程調用(Asynchronous Procedure Calls,APCs)
(1)使用overlapped I/O與event搭配的兩個問題:
<1>WaitForMultipleObjects最多等待64個對象。
<2>必須不斷的根據“哪一個handle被激發”而計算如何反應。
(2)使用Ex版的ReadFile和WriteFile,可以使用異步過程調用機制。只有當線程處于alertable狀態時,APCs才會被調用。當線程因為以下5個函數而處于等待狀態,且線程的“alertable”標記被設為TRUE,則線程處于alertable狀態:
SleepEx()
WaitForSingleObjectEx()
WaitForMultipleObjectEx()
MsgWaitForMultipleObjectsEx()
SignalObjectAndWait();
(3)用于 overlapped I/O 的 APCs 是一種所謂的 user mode APCs。WindowsNT 另有一種所謂的 kernel mode APCs。Kernel mode APCs 也會像 usermode APCs 一樣被保存起來,但一個 kernel mode APC 一定會在下一個timeslice 被調用,不管線程當時正在做什么。 Kernel mode APCs 用來處理系統機能,不在應用程序的控制之中。
(4)提供的 I/O completion routine 應該有這樣的型式:
VOID WINAPI FileIOCompletionRoutine(
DWORD dwErrorCode, ? //0表示操作完成,ERROR_HANDLE_EOF表示操作已經到了文件尾端。
DWORD dwNumberOfBytesTransferred,//真正被傳輸的數據字節數
LPOVERLAPPED lpOverlapped//指向overlapped結構,此結構由開啟overlapped I/O操作的函數提供
);
(5)使用 APCs 時,OVERLAPPED 結構中的 hEvent 欄位不需要用來放置一個 event handle。Win32 文件上說此時 hEvent 欄位可以由程序員自由運用。那么最大的用途就是:首先配置一個結構,描述數據來自哪里,或是要對數據進行一些什么操作,然后將 hEvent 欄位設定指向該結構
(6)在C++ 中產生一個I/O Completion Routines:儲存一個指針,指向用戶自定義數據(一個對象),然后經由此指針調用一個 C++ 成員函數。由于 static 成員函數是類的一部分,你還是可以調用 private 成員函數。
6.對文件進行overlapped I/O的缺點
(1)似乎 Windows NT 是以“I/O 請求”的大小來決定要不要將此請求先記錄下來。所以對于數據量小的操作,overlapped I/O的效率反而更低。
(2)解決辦法:以少量的線程負責所有的硬盤 I/O,然后把這些線程的I/O 請求,保持在一個隊列之中。這種效率比較高。
(3)有兩種情況,overlapped I/O 總是同步執行,甚至即使 FILE_FLAG_NO_BUFFERING 已經指定。第一種情況是你進行一個寫入操作而造成文件的擴展。第二種情況是你讀寫一個壓縮文件。
7.I/O Completion Ports
(1)APCs的缺點:最大的問題就是,有好幾個 I/O APIs 并不支持 APCs,如listen() 和 WaitCommEvent() 便是兩個例子。APCs 的另一個問題是,只有發出“overlapped 請求”的那個線程才能夠提供 callback 函數,然而在一個“scalable”(譯注)系統中,最好任何線程都能夠服務 events。
(2)產生一個I/O Completion Port
HANDLE CreateIoCompletionPort(
HANDLE FileHandle, ? ? ? ? ? ? ? ? ? ? ? ?//文件或設備的handle,若為INVALID_HANDLE_VALUE,則產生一個沒有和任何handle關聯的port。
HANDLE ExistingCompletionPort, ? ? ?//若此欄位被指定,則FileHandle被加到此port上。指定Null產生一個新的port。
DWORD CompletionKey, ? ? ? ? ? ? ? ? ?//用戶自定義的一個數值,將被交給提供服務的線程。此值和FileHanlde有關聯。
DWORD NumberOfConcurrentThreads//與此I/O completion port 有關聯的線程個數。
);
(3)與一個文件handle產生關聯
再次使用CreateIoCompletionPort接口。
(4)在一個I/O Completion Port上等待
BOOL GetQueuedCompletionStatus(
HANDLE CompletionPort, ? ? ? ? ? ? ? ? ? ? ? ?//將在其上等待completion port。
LPDWORD lpNumberOfBytesTransferred, ?//指向DWORD,收到“被傳輸的數據字節數”。
LPDWORD lpCompletionKey, ? ? ? ? ? ? ? ? ? //指向DWORD,該DWORD將收到由CreateIoCompletionPort定義的key。
LPOVERLAPPED *lpOverlapped, ? ? ? ? ? ? ? //overlapped結構指針的地址。
DWORD dwMilliseconds ? ? ? ? ? ? ? ? ? ? ? ? ?//等待的最長時間,時間終了,lpOverlapped被設為NULL,函數傳回FALSE。
);
在completion port上等待的線程是以先進后出的次序提供服務。
(5)避免Completion Packets
設定一個 OVERLAPPED 結構,內含一個合法的手動重置(manual-reset)event 對象,放在 hEvent 欄位。然后把該 handle 的最低位設為 1。
overlap.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
overlap.hEvent = (HANDLE)((DWORD)overlap.hEvent | 0x1);
WriteFile(hFile, buffer, 128,& dwBytesWritten, &overlap);
8.對Sockets使用Overlapped I/O
分析ECHO例子,多實踐socket IOCP。
轉載于:https://www.cnblogs.com/programmer-wfq/p/4646151.html
總結
- 上一篇: 莫队算法 BOJ 2038 [2009国
- 下一篇: BZOJ 3436: 小K的农场( 差分