《Win32多线程程序设计》(12)---overlapped I/O
2014-02-12 17:21
323 查看
文字版电子书-----请点击这里
overlapped I/O 是Win32 的一项技术,你可以要求操作系统为你传送数据,并且在传送完毕时通知你。这项技术使你的程序在I/O 进行过程中仍然能够继续处理事务。
i 激发的文件handles
i 激发的event 对象
i 异步过程调用(Asynchronous Procedure Calls,APCs)
i I/O completion ports
iCreateFile()
iReadFile()
iWriteFile()
CreateFile() 可以用来打开各式各样的资源,包括(但不限制于):
i 文件(硬盘、软盘、光盘或其他)
i 串行口和并行口(serial and parallel ports)
iNamed pipes
iConsole
其中第6个参数dw FlagsAndAttributes 是使用overlapped I/O 的 关 键。这个参数可以藉由许多个数值组合在一起而完成,其中对于本处讨论最重要的一个数值便是FILE_ FLAG_OVERLAPPED。你可以藉着这个参数,指定使用同步(传统的)调用,或是使用overlapped(异步)调用,但不能够两个都指定。
换句话说,如果这个标记值设立,那么对该文件的每一个操作都将是overlapped。
一个不常被讨论的overlapped I/O 性质是,它可以在同一时间读(或写)文件的许多部分。微妙处在于这些操作都使用相同的文件handle。因此,当你使用overlapped I/O时,没有所谓“目前的文件位置”这样的观念。每一次读或写的操作都必须包含其文件位置。
如果你发出许多个overlapped 请求,那么执行次序无法保证。虽然你在单一磁盘中对文件进行操作时很少会有这样的行为,但如果面对多个磁盘,或不同种类的设备(如网络和磁盘),就常常会看到I/O 请求完全失去次序。
OVERLAPPED 结构看起来像这样:
typedef struct _OVERLAPPED {
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;
} OVERLAPPED, *LPOVERLAPPED;
Internal 通常它被保留。然而当GetOverlappedResult() 传回F ALSE 并且GetLastError() 并非传回ERROR_IO_PENDING 时,这个栏位将内含一个视系统而定的状态
InternalHigh 通常它被保留。然而当GetOverlappedResult() 传回TRUE时,这个栏位将内含“被传输数据的长度”
Offset 文件之中开始被读或被写的偏移位置(以字节为单位)。该偏移位置从文件头开始起算。如果目标设备(例如pipes)并没有支持文件位置,此栏位将被忽略
OffsetHigh 64 位的文件偏移位置中,较高的32 位。如果目标设备(例如pipes)并没有支持文件位置,此栏位将被忽略
hEvent 一个手动重置(manual-reset)的event 对象,当overlapped I/O 完成时即被激发。ReadFileEx() 和W riteFileEx() 会忽略这个栏位,彼时它可能被用来传递一个用户自定义的指针
如果你需要等待overlapped I/O 的执行结果,作为WaitForMultipleObjects() 的一部分,请在handle 数组中加上这个文件handle。文件handle 是一个核心对象,一旦操作完毕即被激发。当你完成操作之后,请调用G etOverlappedResult() 以确定结果如何。
调用G etOverlappedResult(),你获得的结果和“调用ReadFile() 或WriteFile() 而没有指定overlapped I/O”所传回的东西一样。这个函数的价值在于,在文件操作真正完成之前,你不可能确实知道它是否成功。甚至在一个完美无瑕的环境下读一个已知的磁盘文件,也有可能发生硬件错误、服务器当掉,或任何未能预期的错误。因此,调用G etOverlappedResult() 是很重要的。
参数
hFile 文件或设备(device)的handle。
lpOverlapped 一个指针,指向OVERLAPPED 结构。
lpNumberOfBytesTransferred 一个指针,指向DWORD,用以表示真正被传输的字节个数。
bWait 一个布尔值,用以表示是否要等待操作完成。TRUE 表示要等待。
返回值
如果overlapped 操作成功,此函数传回TRUE。失败则传回FALSE。GetLastError() 可获得更详细的失败信息。如果bWait 为FALSE 而overlapped 还是没有完成,GetLastError() 会传回ERROR_IO_INCOMPLETE。
有一件事很重要:你所使用的event 对象必须是手动重置(manual-reset)而非自动重置(auto-reset,详见第4章)。如果你使用自动重置,就可能产生出race condition ,因为系统核心有可能在你有机会等待该event 对象之前,先激发它,而你知道,event 对象的激发状态是不能够保留的(这一点和semaphore 不同)。于是这个event 状态将遗失,而你的Wait...() 函数永不返回。但是一个手动重置的event,一旦激发,就一直处于激发状态,直到你动手将它改变。
使用event 对象搭配overlapped I/O,你就可以对同一个文件发出多个读取操作和多个写入操作,每一个操作有自己的event 对象;然后再调用WaitForMultipleObjects() 来等待其中之一(或全部)完成。
overlapped I/O 是Win32 的一项技术,你可以要求操作系统为你传送数据,并且在传送完毕时通知你。这项技术使你的程序在I/O 进行过程中仍然能够继续处理事务。
i 激发的文件handles
i 激发的event 对象
i 异步过程调用(Asynchronous Procedure Calls,APCs)
i I/O completion ports
Win32 文件操作函数
Win32 之中有三个基本的函数用来执行I/O,它们是:iCreateFile()
iReadFile()
iWriteFile()
CreateFile() 可以用来打开各式各样的资源,包括(但不限制于):
i 文件(硬盘、软盘、光盘或其他)
i 串行口和并行口(serial and parallel ports)
iNamed pipes
iConsole
HANDLE CreateFile( LPCTSTR lpFileName, // 指向文件名称 DWORD dwDesiredAccess, // 存取模式(读或写) DWORD dwShareMode, // 共享模式(share mode) LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 指向安全属性结构 DWORD dwCreationDisposition, // 如何产生 DWORD dwFlagsAndAttributes, // 文件属性 HANDLE hTemplateFile // 一个临时文件,将拥有全部的属性拷贝 );
其中第6个参数dw FlagsAndAttributes 是使用overlapped I/O 的 关 键。这个参数可以藉由许多个数值组合在一起而完成,其中对于本处讨论最重要的一个数值便是FILE_ FLAG_OVERLAPPED。你可以藉着这个参数,指定使用同步(传统的)调用,或是使用overlapped(异步)调用,但不能够两个都指定。
换句话说,如果这个标记值设立,那么对该文件的每一个操作都将是overlapped。
一个不常被讨论的overlapped I/O 性质是,它可以在同一时间读(或写)文件的许多部分。微妙处在于这些操作都使用相同的文件handle。因此,当你使用overlapped I/O时,没有所谓“目前的文件位置”这样的观念。每一次读或写的操作都必须包含其文件位置。
如果你发出许多个overlapped 请求,那么执行次序无法保证。虽然你在单一磁盘中对文件进行操作时很少会有这样的行为,但如果面对多个磁盘,或不同种类的设备(如网络和磁盘),就常常会看到I/O 请求完全失去次序。
BOOL ReadFile( HANDLE hFile, // 欲读之文件 LPVOID lpBuffer, // 接收数据之缓冲区 DWORD nNumberOfBytesToRead, // 欲读取的字节个数 LPDWORD lpNumberOfBytesRead, // 实际读取的字节个数的地址 LPOVERLAPPED lpOverlapped // 指针,指向overlapped info ); BOOL WriteFile( HANDLE hFile, // 欲写之文件 LPCVOID lpBuffer, // 储存数据之缓冲区 DWORD nNumberOfBytesToWrite, // 欲写入的字节个数 LPDWORD lpNumberOfBytesWritten, // 实际写入的字节个数的地址 LPOVERLAPPED lpOverlapped // 指针,指向overlapped info );这两个函数很像C runtime 函数中的fread() 和fwrite(),差别在于最后一个参数lpOverlapped。如果CreateFile() 的第6个参数被指定为FILE_FLAG_ OVERLAPPED,你就必须在上述的lpO verlapped 参数中提供一个指针,指向一个OVERLAPPED 结构。
OVERLAPPED 结构
OVERLAPPED 结构执行两个重要的功能。第一,它像一把钥匙,用以识别每一个目前正在进行的overlapped 操作。第二,它在你和系统之间提供了一个共享区域,参数可以在该区域中双向传递。OVERLAPPED 结构看起来像这样:
typedef struct _OVERLAPPED {
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;
} OVERLAPPED, *LPOVERLAPPED;
Internal 通常它被保留。然而当GetOverlappedResult() 传回F ALSE 并且GetLastError() 并非传回ERROR_IO_PENDING 时,这个栏位将内含一个视系统而定的状态
InternalHigh 通常它被保留。然而当GetOverlappedResult() 传回TRUE时,这个栏位将内含“被传输数据的长度”
Offset 文件之中开始被读或被写的偏移位置(以字节为单位)。该偏移位置从文件头开始起算。如果目标设备(例如pipes)并没有支持文件位置,此栏位将被忽略
OffsetHigh 64 位的文件偏移位置中,较高的32 位。如果目标设备(例如pipes)并没有支持文件位置,此栏位将被忽略
hEvent 一个手动重置(manual-reset)的event 对象,当overlapped I/O 完成时即被激发。ReadFileEx() 和W riteFileEx() 会忽略这个栏位,彼时它可能被用来传递一个用户自定义的指针
(1)被激发的 File Handles
最简单的overlapped I/O 类型,是使用它自己的文件handle 作为同步机制。首先你以FILE_ FLAG_OVERLAPPED 告诉Win32 说你不要使用默认的同步I/O。然后,你设立一个OVERLAPPED 结构,其中内含“I/O 请求”的所有必要参数,并以此识别这个“I/O 请求”,直到它完成为止。接下来,调用ReadFile() 并以OVERLAPPED 结构的地址作为最后一个参数。这时候,理论上,Win32 会在后台处理你的请求。你的程序可以放心地继续处理其他事情。如果你需要等待overlapped I/O 的执行结果,作为WaitForMultipleObjects() 的一部分,请在handle 数组中加上这个文件handle。文件handle 是一个核心对象,一旦操作完毕即被激发。当你完成操作之后,请调用G etOverlappedResult() 以确定结果如何。
调用G etOverlappedResult(),你获得的结果和“调用ReadFile() 或WriteFile() 而没有指定overlapped I/O”所传回的东西一样。这个函数的价值在于,在文件操作真正完成之前,你不可能确实知道它是否成功。甚至在一个完美无瑕的环境下读一个已知的磁盘文件,也有可能发生硬件错误、服务器当掉,或任何未能预期的错误。因此,调用G etOverlappedResult() 是很重要的。
BOOL GetOverlappedResult( HANDLE hFile, LPOVERLAPPED lpOverlapped, LPDWORD lpNumberOfBytesTransferred, BOOL bWait );
参数
hFile 文件或设备(device)的handle。
lpOverlapped 一个指针,指向OVERLAPPED 结构。
lpNumberOfBytesTransferred 一个指针,指向DWORD,用以表示真正被传输的字节个数。
bWait 一个布尔值,用以表示是否要等待操作完成。TRUE 表示要等待。
返回值
如果overlapped 操作成功,此函数传回TRUE。失败则传回FALSE。GetLastError() 可获得更详细的失败信息。如果bWait 为FALSE 而overlapped 还是没有完成,GetLastError() 会传回ERROR_IO_INCOMPLETE。
#define WIN32_LEAN_AND_MEAN #include <stdio.h> #include <stdlib.h> #include <windows.h> // // Constants // #define READ_SIZE 512 // // Function prototypes // void CheckOsVersion(); int main() { BOOL rc; HANDLE hFile; DWORD numread; OVERLAPPED overlap; char buf[READ_SIZE]; char szPath[MAX_PATH]; CheckOsVersion(); GetWindowsDirectory(szPath, sizeof(szPath)); strcat(szPath, "\\WINHLP32.EXE"); // Open the file for overlapped reads hFile = CreateFile( szPath, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL ); if (hFile == INVALID_HANDLE_VALUE) { printf("Could not open %s\n", szPath); return -1; } // Initialize the OVERLAPPED structure memset(&overlap, 0, sizeof(overlap)); overlap.Offset = 1500; // Request the data rc = ReadFile( hFile, buf, READ_SIZE, &numread, &overlap ); printf("Issued read request\n"); // Was the operation queued? if (rc) { // The data was read successfully printf("Request was returned immediately\n"); } else { if (GetLastError() == ERROR_IO_PENDING) { // We could do something else for awhile here... printf("Request queued, waiting...\n"); WaitForSingleObject(hFile, INFINITE); printf("Request completed.\n"); rc = GetOverlappedResult( hFile, &overlap, &numread, FALSE ); printf("Result was %d\n", rc); } else { // We should check for memory and quota // errors here and retry. See the samples // IoByEvnt and IoByAPC. // Something went wrong printf("Error reading file\n"); } } CloseHandle(hFile); return EXIT_SUCCESS; } // // Make sure we are running under an operating // system that supports overlapped I/O to files. // void CheckOsVersion() { OSVERSIONINFO ver; BOOL bResult; ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); bResult = GetVersionEx((LPOSVERSIONINFO) &ver); if ( (!bResult) || (ver.dwPlatformId != VER_PLATFORM_WIN32_NT) ) { fprintf(stderr, "IoByFile must be run under Windows NT.\n"); exit(EXIT_FAILURE); } }
被激发的 Event 对象
OVERLAPPED 结构中的最后一个栏位,是一个event handle。如果你使用文件handle 作为激发对象,那么此栏位可为NULL。当这个栏位被设定为一个event 对象时,系统核心会在overlapped 操作完成的时候,自动将此event 对象给激发起来。由于每一个overlapped 操作都有它自己独一无二的OVERLAPPED 结构,所以每一个结构都有它自己独一无二的一个event 对象,用以代表该操作。有一件事很重要:你所使用的event 对象必须是手动重置(manual-reset)而非自动重置(auto-reset,详见第4章)。如果你使用自动重置,就可能产生出race condition ,因为系统核心有可能在你有机会等待该event 对象之前,先激发它,而你知道,event 对象的激发状态是不能够保留的(这一点和semaphore 不同)。于是这个event 状态将遗失,而你的Wait...() 函数永不返回。但是一个手动重置的event,一旦激发,就一直处于激发状态,直到你动手将它改变。
使用event 对象搭配overlapped I/O,你就可以对同一个文件发出多个读取操作和多个写入操作,每一个操作有自己的event 对象;然后再调用WaitForMultipleObjects() 来等待其中之一(或全部)完成。
#define WIN32_LEAN_AND_MEAN #include <stdio.h> #include <stdlib.h> #include <windows.h> #include "MtVerify.h" // // Constants // #define MAX_REQUESTS 5 #define READ_SIZE 512 #define MAX_TRY_COUNT 5 // // Function prototypes // int QueueRequest(int nIndex, DWORD dwLocation, DWORD dwAmount); void CheckOsVersion(); // // Global variables // // Need to keep the events in their own array // so we can wait on them. HANDLE ghEvents[MAX_REQUESTS]; // Keep track of each individual I/O operation OVERLAPPED gOverlapped[MAX_REQUESTS]; // Handle to the file of interest. HANDLE ghFile; // Need a place to put all this data char gBuffers[MAX_REQUESTS][READ_SIZE]; int main() { int i; BOOL rc; char szPath[MAX_PATH]; CheckOsVersion(); GetWindowsDirectory(szPath, sizeof(szPath)); strcat(szPath, "\\WINHLP32.EXE"); // Open the file for overlapped reads ghFile = CreateFile( szPath, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL ); if (ghFile == INVALID_HANDLE_VALUE) { printf("Could not open %s\n", szPath); return -1; } for (i=0; i<MAX_REQUESTS; i++) { // Read some bytes every few K QueueRequest(i, i*16384, READ_SIZE); } printf("QUEUED!!\n"); // Wait for all the operations to complete. MTVERIFY( WaitForMultipleObjects( MAX_REQUESTS, ghEvents, TRUE, INFINITE ) != WAIT_FAILED ); // Describe what just happened. for (i=0; i<MAX_REQUESTS; i++) { DWORD dwNumread; rc = GetOverlappedResult( ghFile, &gOverlapped[i], &dwNumread, FALSE ); printf("Read #%d returned %d. %d bytes were read.\n", i, rc, dwNumread); CloseHandle(gOverlapped[i].hEvent); } CloseHandle(ghFile); return EXIT_SUCCESS; } /* * Call ReadFile to start an overlapped request. * Make sure we handle errors that are recoverable. * Properly set up the event object for this operation. */ int QueueRequest(int nIndex, DWORD dwLocation, DWORD dwAmount) { int i; BOOL rc; DWORD dwNumread; DWORD err; MTVERIFY( ghEvents[nIndex] = CreateEvent( NULL, // No security TRUE, // Manual reset - extremely important! FALSE, // Initially set Event to non-signaled state NULL // No name ) ); gOverlapped[nIndex].hEvent = ghEvents[nIndex]; gOverlapped[nIndex].Offset = dwLocation; for (i=0; i<MAX_TRY_COUNT; i++) { rc = ReadFile( ghFile, gBuffers[nIndex], dwAmount, &dwNumread, &gOverlapped[nIndex] ); // Handle success if (rc) { printf("Read #%d completed immediately.\n", nIndex); return TRUE; } err = GetLastError(); // Handle the error that isn't an error. rc is zero here. if (err == ERROR_IO_PENDING) { // asynchronous i/o is still in progress printf("Read #%d queued for overlapped I/O.\n", nIndex); return TRUE; } // Handle recoverable error if ( err == ERROR_INVALID_USER_BUFFER || err == ERROR_NOT_ENOUGH_QUOTA || err == ERROR_NOT_ENOUGH_MEMORY ) { Sleep(50); // Wait around and try later continue; } // Give up on fatal error. break; } printf("ReadFile failed.\n"); return -1; } // // Make sure we are running under an operating // system that supports overlapped I/O to files. // void CheckOsVersion() { OSVERSIONINFO ver; BOOL bResult; ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); bResult = GetVersionEx((LPOSVERSIONINFO) &ver); if ( (!bResult) || (ver.dwPlatformId != VER_PLATFORM_WIN32_NT) ) { fprintf(stderr, "IoByEvnt must be run under Windows NT.\n"); exit(EXIT_FAILURE); } }
相关文章推荐
- 《Win32多线程程序设计》学习笔记 第6章 Overlapped I/o 在你的身后变戏法
- 思考mysql内核之初级系列12---innodb的簇描述结构
- 黑马程序员_12_7k面试题之交通灯
- 小娜学法(12):什么是案件信息公开?
- VMware Workstation 12专业版永久序列号(任选其一)
- UG OPEN API编程基础 12UIStyler对话框
- 火狐浏览器—FlashPlayerPlugin_12_0_0_44.exe 应用程序错误
- RHCE 学习笔记(12) 归档压缩和文件传递
- C++ 12 —— 初始化顺序
- 从面值为 1,4,12,21的钱币中最多取五张,产生1~n的连续的数目,问这个n有多大(华为)
- Linux 学习笔记_12_文件共享服务_3_NFS网络文件服务
- Entity Framework 6 Recipes 2nd Edition(12-5)译 -> 自动删除相关联实体
- VMware 12 安装 macOS Sierra 0.12.4
- 12、宏,define,和main函数
- Linux 学习笔记_12_文件共享服务_4_SSH
- 架构设计:系统间通信(12)——RPC实例Apache Thrift 中篇
- 12. Integer to Roman
- 校园数字化建设--注册中心投标文件研究(12)--接口文档
- 黑马程序言 Java基础学习笔记12 线程的创建
- 2015 12 3 Handler