http://www.cppblog.com/Lee7/archive/2008/01/07/40650.html
Overlapped I/O也称Asynchronous I/O,异步I/O模型。异步I/O和同步I/O不同,同步I/O时,程序被挂起,一直到I/O处理完,程序才能获得控制。异步I/O,调用一个函数告诉 OS,进行I/O操作,不等I/O结束就立即返回,继续程序执行,操作系统完成I/O之后,通知消息给你。Overlapped I/O只是一种模型,它可以由内核对象(hand),事件内核对象(hEvent), 异步过程调用(apcs) 和完成端口(I/O completion)实现。Overlapped I/O的设计的目的:
取代多线程功能,(多线程存在同步机制,错误处理,在成千上万个线程I/O中,线程上下文切换是十分消耗CPU资源的)。Overlapped I/O模型是OS为你传递数据,完成上下文切换,在处理完之后通知你。由程序中的处理,变为OS的处理。内部也是用线程处理的。
Overlapped数据结构功能
1. 标识每个正在overlapped 的操作。2. 程序和系统之间提供了共享区域。参数可以在区域内双向传递。
OVERLAPPED和数据缓冲区释放问题:在请求时,不能释放,只有在I/O请求完成之后,才可以释放。如果发出多个overlapped请求,每个overlapped读写操作,都必须包含文件位置(socket),另外,如果有多个磁盘,I/O执行次序无法保证。(每个overlapped都是独立的请求操作)。
例子:用overlapped模型读一个磁盘文件内容。
1.把设备句柄看作同步对象,ReadFile将设备句柄设为无信号。ReadFile 异步I/O字节位置必须在OVERLAPPED结构中指定。
2.完成I/O,设置信息状态。为有信号。
3.WaitForSingleObject或WaitForMultipleObject判断或者异步设备调用GetOverLappedResult函数。
内核对象(hand)实现的问题:不能区分那一个overlapped操作,对同一个文件handle,系统有多个异步操作时(一边读文件头,一边写文件尾, 有一个完成,就会有信号,不能区分是那种操作。),为每个进行中的overlapped调用GetOverlappedResult是不好的作法。
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" ); 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; } memset( &overlap, 0, sizeof( overlap ) ); overlap.Offset = 1500; rc = ReadFile( hFile, buf, READ_SIZE, &numread, &overlap ); // ReadFile将设备句柄设为无信号 printf( "Issued read request\n" ); if ( rc ) { printf( "Request was returned immediately\n" ); } else { if ( GetLastError( ) == ERROR_IO_PENDING ) { printf( "Request queued, waiting\n" ); WaitForSingleObject( hFile, INFINITE ); // 完成I/O,设置信息状态。为有信号。 printf( "Request completed.\n" ); rc = GetOverlappedResult( hFile, &overlap, &numread, FALSE ); printf( "Result was %d\n", rc ); } else { printf( "Error reading file\n" ); } } CloseHandle( hFile ); return EXIT_SUCCESS; }Overlapped成员hEven标识事件内核对象。CreateEvent,为每个请求创建一个事件,初始化每个请求的hEvent成员(对同一文件多个读写请求,每个操作绑定一个event对象)。调用WaitForMultipleObject来等等其中一个(或全部)完成。
另外Event对象必须是手动重置。使用自动重置(在等待event之前设置,WaitForSingleObject()和 WaitForMultipleObjects()函数永不返回)。自动重置事件 WaitForSingleObject()和 WaitForMultipleObjects()会等待事件到信号状态,随后又自动将其重置为非信号状态,这样保证了等待此事件的线程中只有一个会被唤醒。手动重置事件需要用户调用ResetEvent()才会重置事件。可能有若干个线程在等待同一事件, 这样当事件变为信号状态时,所有等待线程都可以运行了。 SetEvent()函数用来把事件对象设置成信号状态,ResetEvent()把事件对象重置成非信号状态,两者均需事件对象句柄作参数。
事件内核对象(hEvent)的问题:事件内核对象在使用WaitForMultipleObjects时,只能等待64个对象。需要另建两个数据组,并gOverlapped[nIndex].hEvent = ghEvents[nIndex]绑定起来。
HANDLE WINAPI CreateFile( __in LPCTSTR lpFileName, __in DWORD dwDesiredAccess, __in DWORD dwShareMode, __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes, __in DWORD dwCreationDisposition, __in DWORD dwFlagsAndAttributes, __in_opt HANDLE hTemplateFile );
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全属性 BOOL bManualReset, // 复位方式 BOOL bInitialState, // 初始状态 LPCTSTR lpName // 对象名称 );
DWORD WINAPI WaitForSingleObject( __in HANDLE hHandle, __in DWORD dwMilliseconds ); BOOL WINAPI GetOverlappedResult( HANDLE hFile, LPOVERLAPPED lpOverlapped, LPDWORD lpNumberOfBytesTransferred, BOOL bWait ) ; 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" ); 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; } memset( &overlap, 0, sizeof( overlap ) ); overlap.Offset = 1500; // 创建一个有名的,不能被继承的,手动复原,初始状态是无信号状态的事件对象 for overlapped I/O overlap.hEvent = CreateEvent( NULL, TRUE, FALSE, 0 ); rc = ReadFile( hFile, buf, READ_SIZE, &numread, &overlap ); printf( "Issued read request\n" ); if ( rc ) { printf( "Request was returned immediately\n" ); } else { if ( GetLastError( ) == ERROR_IO_PENDING ) { printf( "Request queued, waiting\n" ); WaitForSingleObject( overlap.hEvent, INFINITE ); printf( "Request completed.\n" ); rc = GetOverlappedResult( hFile, &overlap, &numread, FALSE ); printf( "Result was %d\n", rc ); } else { printf( "Error reading file\n" ); } } CloseHandle( overlap.hEvent ); CloseHandle( hFile ); return EXIT_SUCCESS; }
异步过程调用,callback回调函数,在一个Overlapped I/O完成之后,系统调用该回调函数。OS在有信号状态下(设备句柄),才会调用回调函数(可能有很多APCS等待处理了),传给它完成I/O请求的错误码,传输字节数和Overlapped结构的地址。
异步过程调用(apcs)问题:只有发overlapped请求的线程才可以提供callback函数(需要一个特定的线程为一个特定的I/O请求服务)。
五个函数可以设置信号状态:1.SleepEx2.WaitForSingleObjectEx3.WaitForMultipleObjectEx4.SingalObjectAndWait5.MsgWaitForMultipleObjectsExMain函数调用WaitForSingleObjectEx, APCS被处理,调用回调函数FileIOCompletionRoutine
VOID WINAPI FileIOCompletionRoutine( DWORD dwErrorCode, // completion code DWORD dwNumberOfBytesTransfered, // number of bytes transferred LPOVERLAPPED lpOverlapped // pointer to structure with I/O information ) { int nIndex = (int) ( lpOverlapped->hEvent ); printf( "Read #%d returned %d. %d bytes were read.\n", nIndex, dwErrorCode, dwNumberOfBytesTransfered ); if ( ++nCompletionCount == MAX_REQUESTS ) SetEvent( ghEvent ); // Cause the wait to terminate } int main( ) { int i; char szPath[ MAX_PATH ]; CheckOsVersion( ); MTVERIFY( ghEvent = CreateEvent( NULL, // No security TRUE, // Manual reset - extremely important! FALSE, // Initially set Event to non-signaled state NULL // No name ) ); GetWindowsDirectory( szPath, sizeof( szPath ) ); strcat( szPath, "\\WINHLP32.EXE" ); 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++ ) { QueueRequest( i, i * 16384, READ_SIZE ); } printf( "QUEUED!!\n" ); for ( ;; ) { DWORD rc; rc = WaitForSingleObjectEx( ghEvent, INFINITE, TRUE ); if ( rc == WAIT_OBJECT_0 ) break; MTVERIFY( rc == WAIT_IO_COMPLETION ); } CloseHandle( ghFile ); return EXIT_SUCCESS; } int QueueRequest( int nIndex, DWORD dwLocation, DWORD dwAmount ) { int i; BOOL rc; DWORD err; gOverlapped[ nIndex ].hEvent = (HANDLE) nIndex; gOverlapped[ nIndex ].Offset = dwLocation; for ( i = 0; i < MAX_TRY_COUNT; i++ ) { rc = ReadFileEx( ghFile, gBuffers[ nIndex ], dwAmount, &gOverlapped[ nIndex ], FileIOCompletionRoutine ); if ( rc ) { printf( "Read #%d queued for overlapped I/O.\n", nIndex ); return TRUE; } err = GetLastError( ); if ( err == ERROR_INVALID_USER_BUFFER || err == ERROR_NOT_ENOUGH_QUOTA || err == ERROR_NOT_ENOUGH_MEMORY ) { Sleep( 50 ); // Wait around and try later continue; } break; } printf( "ReadFileEx failed.\n" ); return -1; }
转载于:https://www.cnblogs.com/shangdawei/archive/2013/04/19/3030827.html
相关资源:数据结构—成绩单生成器