本章整理overlapped I/O 也就是异步I/O(asynchronous I/O)的内容。
overlapped I/O + completion ports 常被设计为多线程处理,可在I/O bound的程序中获得高效率。
众所周知:I/O设备是个慢速设备,让CPU干等I/O的完成不是一件明智的事情,因此一个解决方案就是,使用另一个线程来进行I/O,然而,这会产生一些相关的问题,就是如何在主线程中操控许多个worker线程、如何设定同步机制、如何处理错误情况、如何显示对话框等。
异步I/O是win32的一项技术,你可以要求操作系统为你传送数据,并且在传送完毕时通知你,操作系统内部正是以线程来完成异步I/O的。
Win32文件操作函数
win32 中有3个基本的函数用来执行I/O,它们是:
- CreateFile()
- ReadFile()
- WriteFile()
没有专门的函数用来关闭文件,只要调用CloseHandle()即可。
CreateFile
用来打开各种资源包括文件,串行口和并行口,Named pipes,console
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
);
参数1:文件名称
参数2:存取模式:The most commonly used values are GENERIC_READ, GENERIC_WRITE, or both (GENERIC_READ | GENERIC_WRITE).
参数3:共享模式
详细见:https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
参数4:指向安全属性结构
参数5:如何产生
参数6:文件属性,异步需要设置为FILE_FLAG_OVERLAPPED
参数7: 一个临时文件,将拥有全部的属性拷贝
一个不常被讨论的性质是,overlapped I/O可以在同一时间读文件的许多部分,这些操作都是用相同的文件handle,因此当你使用overlapped I/O时,没有所谓的目前的文件位置的这样的观念。
如果你发出多个overlapped请求
不能通过调用C runtime library 中的stdio.h函数来使用overlapped I/O。
ReadFile
BOOL WINAPI ReadFile(
_In_ HANDLE hFile,
_Out_ LPVOID lpBuffer,
_In_ DWORD nNumberOfBytesToRead,
_Out_opt_ LPDWORD lpNumberOfBytesRead,
_Inout_opt_ LPOVERLAPPED lpOverlapped
);
参数1:将要读的文件
参数2:想要接收数据的缓冲区
参数3:欲读取的字节个数
参数4:实际读取的字节个数的地址
参数5:指针,指向overlapped info
这个函数很想c runtime函数中的fread(),差别在于最后一个参数lpOverlapped,如果CreateFile()第6个参数被指定为FILE_FLAG_OVERLAPPED
,你就必须在上述的lpOverlapped参数中提供一个指针,指向一个OVERLAPPED结构。
WriteFile
BOOL WINAPI WriteFile(
_In_ HANDLE hFile,
_In_ LPCVOID lpBuffer,
_In_ DWORD nNumberOfBytesToWrite,
_Out_opt_ LPDWORD lpNumberOfBytesWritten,
_Inout_opt_ LPOVERLAPPED lpOverlapped
);
参数1:将要写的文件
参数2:存储数据的缓冲区
参数3:欲写入的字节个数
参数4:实际写入的字节个数的地址
参数5:指针,指向overlapped info
OVERLAPPED结构
上文中提到的指针指向了一个OVERLAPPED结构,这个结构有什么用呢?
它主要起到两方面的作用:
1. 识别目前正在进行的overlapped操作
2. 在你和系统之间提供了一个共享区域,参数可以在该区域中双向传递。
typedef struct _OVERLAPPED {
ULONG_PTR Internal;
ULONG_PTR InternalHigh;
union {
struct {
DWORD Offset;
DWORD OffsetHigh;
};
PVOID Pointer;
};
HANDLE hEvent;
} OVERLAPPED, *LPOVERLAPPED;
参数1:通常被保留
参数2:通常被保留,当GetOverLappedResult()函数传回true时,这个栏目将内含被传输数据的长度
参数3:文件之中开始被读或被写的偏移位置,该偏移位置从文件头开始起算。如果目标设备没有支持文件位置,比如pipes,此栏位将被忽略
参数4:64位的文件偏移位置中,较高的32位,如果目标设备没有支持文件位置,比如pipes,此栏位将被忽略
参数5:一个manual-reset的event对象,当overlapped I/O完成时即被激发,ReadFileEx()和WriteFileEx()会忽略这个栏位,此时它可能被用来传递一个用户自定义的指针。
由于OVERLAPPED结构的生命期超越ReadFile()和WriteFile()函数,所以把这个结构放在一个安全的地方是很重要的事情,通常局部变量不是一个安全的地方,因为它会很快超过了生存范围,最安全的地方是heap。
被激发的File Handles
最简单的overlapped I/O类型是使用它自己的文件handle作为同步机制。
如果需要等待overlapped I/O执行结果,作为WaitForMultipleObjects()一部分,请在handles数组中加上这个文件handle,文件handle是一个核心对象,一旦操作完毕即被激发,当你完成操作之后,调用GetOverlappedResult() 确定结果如何。
GetOverLappedResult:
BOOL WINAPI GetOverlappedResult(
_In_ HANDLE hFile,
_In_ LPOVERLAPPED lpOverlapped,
_Out_ LPDWORD lpNumberOfBytesTransferred,
_In_ BOOL bWait
);
参数1: 文件或设备的handle
参数2:一个指针,指向OVERLAPPED结构
参数3:一个指针,指向DWORD,用以表示真正被传输的字节个数
参数4:一个布尔值,用以表示是否要等待操作完成,TRUE表示要等待。
返回值:如果成功返回true,失败返回false。
下面这个程序的表示从文件C:\Windows\WINHLP32.EXE的第1500个位置读取512个字节
/*
* IoByFile.c
*
* Sample code for Multithreading Applications in Win32
* This is from Chapter 6, Listing 6-1
*
* Demonstrates how a file handle becomes signaled
* when an overlapped operation on the handle's file
* is completed. Notice that sometimes the operation
* is completed immediately. The operation is only
* queued, or pended, if ERROR_IO_PENDING is returned.
*/
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
//
// Constants
//
#define READ_SIZE 512
//
// Function prototypes
//