Windows異步I/O詳解
阿新 • • 發佈:2018-03-21
Windows;異步;IO;當我們對文件進行讀寫時,線程本該是阻塞的,即線程在等待讀寫操作的結束,這種方式稱為同步I/O。Windows在系統層為我們提供了一種高效的機制——異步IO。
異步IO提供了這樣一種功能:當你讀取文件時,讀取函數會立刻返回,讀取任務轉交給系統底層自動處理,這樣文件的讀取操作就不會阻塞線程。IO操作完成時,由系統發出完成通知。 :
1.文件變為可等待的對象(即具有激發態和非激發態)
2.文件指針失效(需要通過OVERLAPPED結構體中的的Offect表示讀取或寫入的位置,而不能用SetFilePointer()這樣的函數。)
異步IO提供了這樣一種功能:當你讀取文件時,讀取函數會立刻返回,讀取任務轉交給系統底層自動處理,這樣文件的讀取操作就不會阻塞線程。IO操作完成時,由系統發出完成通知。
介紹三種完成通知:
1.直接等待句柄(文件等待對象)
2.等待OVERLAPPED中的hEvent句柄(等待事件對象)
3.異步調用(APC調用)
主要函數:CreateFile();ReadFile();WriteFile();
數據成員:OVERLAPPED結構體。
一個句柄如果是以異步IO的方式打開,那這個句柄就具有了兩個特性
1.文件變為可等待的對象(即具有激發態和非激發態)
2.文件指針失效(需要通過OVERLAPPED結構體中的的Offect表示讀取或寫入的位置,而不能用SetFilePointer()這樣的函數。)
代碼實例:(VS2015控制臺)
準備:Debug目錄下創建123.exe並保存數據
1.直接等待句柄
#include "stdafx.h" #include <windows.h> typedef struct _MYOVERLAPPED{ OVERLAPPED ol; HANDLE hFile; PBYTE pBuf; int nIndex; }MYOVERLAPPED,*PMYOVERLAPPED; DWORD WINAPI ThreadProc(LPVOID lParam) { PMYOVERLAPPED pol = (PMYOVERLAPPED)lParam; WaitForSingleObject(pol->hFile, INFINITE); for (int i=0;i<10;i++) { printf("%d:%d \n", pol->nIndex,pol->pBuf[i]); } printf("讀完了!\n"); return 0; } int main() { // 1. 異步IO標記 // 有了這個標記 該文件就變為可等待的內核對象 // 後面的read write函數就變為非阻塞的 HANDLE hFile = CreateFile(L"123.txt", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL| FILE_FLAG_OVERLAPPED, NULL); // 2. 文件讀取 PMYOVERLAPPED pol = new MYOVERLAPPED{}; pol->ol.Offset = 0x0;// 從偏移0x100這個位置開始讀 // pol->hEvent == NULL; 系統讀取完成後,會把我的hFile變為有信號狀態 pol->hFile = hFile; pol->pBuf = new BYTE[0x1000]{}; pol->nIndex =1; ReadFile(hFile, pol->pBuf, 0x1000, NULL,//實際讀取的個數,由OVERLAPPED結構體指定 (LPOVERLAPPED)pol); HANDLE hThread = CreateThread(NULL, NULL, ThreadProc, pol, NULL, NULL); PMYOVERLAPPED pol2 = new MYOVERLAPPED{}; pol2->ol.Offset = 0x0;// 從偏移0x100這個位置開始讀 // pol->hEvent == NULL; 系統讀取完成後,會把我的hFile變為有信號狀態 pol2->hFile = hFile; pol2->pBuf = new BYTE[0x1000]{}; pol2->nIndex = 2; ReadFile(hFile, pol2->pBuf, 0x1000, NULL,//實際讀取的個數,由OVERLAPPED結構體指定 (LPOVERLAPPED)pol2); HANDLE hThread2 = CreateThread(NULL, NULL, ThreadProc, pol2, NULL, NULL); // ......幹其他事 WaitForSingleObject(hThread, INFINITE); WaitForSingleObject(hThread2, INFINITE); return 0; }
2.等待事件對象
#include "stdafx.h" #include <windows.h> typedef struct _MYOVERLAPPED { OVERLAPPED ol; HANDLE hFile; PBYTE pBuf; int nIndex; }MYOVERLAPPED, *PMYOVERLAPPED; DWORD WINAPI ThreadProc(LPVOID lParam) { PMYOVERLAPPED pol = (PMYOVERLAPPED)lParam; printf("開始等待......\n"); WaitForSingleObject(pol->ol.hEvent, INFINITE); for (int i = 0; i < 10; i++) { printf("%d:%02x \n", pol->nIndex, pol->pBuf[i]); } printf("讀完了!\n"); return 0; } int main() { // 1. 異步IO標記 // 有了這個標記 該文件就變為可等待的內核對象 // 後面的read write函數就變為非阻塞的 HANDLE hFile = CreateFile(L"..\\Debug\\123.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); // 2. 文件讀取 PMYOVERLAPPED pol = new MYOVERLAPPED{}; pol->ol.Offset = 0x100;// 從偏移0x100這個位置開始讀 pol->ol.hEvent = CreateEvent(NULL,NULL,FALSE,NULL); //系統讀取完成後,會把我的hFile變為有信號狀態 pol->hFile = hFile;// 被無視 pol->pBuf = new BYTE[0x1000]{}; pol->nIndex = 1; ReadFile(hFile, pol->pBuf, 0x1000, NULL,//實際讀取的個數,由OVERLAPPED結構體指定 (LPOVERLAPPED)pol); HANDLE hThread = CreateThread(NULL, NULL, ThreadProc, pol, NULL, NULL); PMYOVERLAPPED pol2 = new MYOVERLAPPED{}; pol2->ol.Offset = 0x200;// 從偏移0x100這個位置開始讀 pol2->ol.hEvent = CreateEvent(NULL, NULL, FALSE, NULL); //系統讀取完成後,會把我的hFile變為有信號狀態 pol2->hFile = hFile;// 被無視 pol2->pBuf = new BYTE[0x1000]{}; pol2->nIndex = 2; ReadFile(hFile, pol2->pBuf, 0x1000, NULL,//實際讀取的個數,由OVERLAPPED結構體指定 (LPOVERLAPPED)pol2); HANDLE hThread2 = CreateThread(NULL, NULL, ThreadProc, pol2, NULL, NULL); // ......幹其他事 WaitForSingleObject(hThread, INFINITE); WaitForSingleObject(hThread2, INFINITE); return 0; }
3.異步調用(APC調用)
異步調用提供了這樣一個機制:你可以在讀取文件或者寫入文件的時候,提供一個回調函數,當讀寫任務完成時,就會調用這個回調函數。
#include "stdafx.h"
#include <windows.h>
typedef struct _MYOVERLAPPED {
OVERLAPPED ol;
HANDLE hFile;
PBYTE pBuf;
int nIndex;
}MYOVERLAPPED, *PMYOVERLAPPED;
// 提交任務的線程處理,其他線程看著
VOID CALLBACK FileIOCompletionRoutine(
_In_ DWORD dwErrorCode,
_In_ DWORD dwNumberOfBytesTransfered,
_Inout_ LPOVERLAPPED lpOverlapped
) {
PMYOVERLAPPED pol = (PMYOVERLAPPED)lpOverlapped;
for (int i = 0; i < 10; i++)
{
printf("%d:%02x \n", pol->nIndex, pol->pBuf[i]);
}
printf("讀完了!\n");
}
int main()
{
// 1. 異步IO標記
// 有了這個標記 該文件就變為可等待的內核對象
// 後面的read write函數就變為非阻塞的
HANDLE hFile = CreateFile(L"..\\Debug\\123.exe", GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
// 2. 文件讀取
PMYOVERLAPPED pol = new MYOVERLAPPED{};
pol->ol.Offset = 0x100;// 從偏移0x100這個位置開始讀
// hEvent被無視 hFile也被無視
//pol->ol.hEvent = CreateEvent(NULL, NULL, FALSE, NULL); //系統讀取完成後,會把我的hFile變為有信號狀態
pol->hFile = hFile;// 被無視
pol->pBuf = new BYTE[0x1000]{};
pol->nIndex = 1;
ReadFileEx(hFile,
pol->pBuf,
0x1000,
(LPOVERLAPPED)pol,
FileIOCompletionRoutine);// 完成後直接調用該回調函數,不用等待文件句柄/事件對象
PMYOVERLAPPED pol2 = new MYOVERLAPPED{};
pol2->ol.Offset = 0x200;// 從偏移0x100這個位置開始讀
//pol2->ol.hEvent = CreateEvent(NULL, NULL, FALSE, NULL); //系統讀取完成後,會把我的hFile變為有信號狀態
//pol2->hFile = hFile;// 被無視
pol2->pBuf = new BYTE[0x1000]{};
pol2->nIndex = 2;
ReadFileEx(hFile,
pol2->pBuf,
0x1000,
(LPOVERLAPPED)pol2,
FileIOCompletionRoutine);
// FileIOCompletionRoutine有系統調用
// 哪個線程執行該函數呢
// 哪個線程read/write 哪個線程執行
// ......幹其他事
// 忙完了 想起來還有兩個函數等著我呢
// CPU檢測到當前線程的APC隊列裏有函數需要執行
// 就去執行該函數,執行完返回
// 只有當第2個參數是TRUE才去執行
SleepEx(200, TRUE);
// WaitForSingleObjectEx()
return 0;
}
Windows異步I/O詳解