1、设备IO与CPU速度甚至是内存访问相比较都是比较慢的,而且更不可预测。虽然如此,通过使用异步设备IO我们仍然能够创造出更高效的程序。
同步IO时,发出IO请求的线程会被挂起。而异步IO时发出请求的线程不会被挂起,而是可以继续执行。异步IO请求传给了设备驱动程序,被加入到驱动程序的请求队列中,驱动程序负责实际的IO操作。当设备驱动程序完成了对队列中IO请求的处理,无论成功与否都必须通知应用程序。
异步IO非常关键的一点就是将IO请求加入驱动程序队列,这是设计高性能可伸缩应用程序的本质所在。
为了以异步的方式来访问设备,首先在调用CreateFile时,必须传给dwFlagsAndAttributes参数
FILE_FLAG_OVERLAPPED标志。这个标志告诉系统我们想要以异步的方式来访问设备。
其次,还需要将IO请求加入设备驱动程序的队列。我们需要ReadFile和WriteFile两个函数。这两个函数会检查hFile参数标识的设备是否FILE_FLAG_OVERLAPPED标志打开的。如果指定了这个标志,那么函数会执行异步设备IO。此时,可以传NULL给CreateFile和WriteFile的pdwNumBytes参数,因为它们会立即返回,检查pdwNumBytes是没有意义的。
再者,必须在pOverlapped参数总传入一个已初始化的OVERLAPPED结构。该结构定义如下:
typedef struct _OVERLAPPED
{
DWORD Internal;//错误代码。DWORD InternalHigh;//已传输字节数。
DWORD Offset;//低32位文件偏移。
DWORD OffsetHigh;//高32位文件偏移。
HANDLE hEvent;//事件对象句柄。
}OVERLAPPED,*LPOVERLAPPED;
---Offset,OffsetHigh,hEvent必须在调用ReadFile和WriteFile前初始化。
---Internal和InternalHigh由驱动程序设置。
---Offset和OffsetHigh共同构成一个64位的偏移量。它表示在访问文件时应该从何处访问设备。
提供偏移量是因为:在执行异步操作时系统会忽略文件指针。如果不再此处指定,那么系统将无法知道下次应该从哪里开始读取数据。为了防止在同一个对象上进行多个异步调用时出现混淆,所有的异步调用IO请求都必须在OVERLAPPED结构中指定起始偏移量。
非文件设备必须为这两个成员都指定为0,否则IO请求将会失败。此时GetLastError返回ERROR_INVALID_PARAMETER。
---hEvent标识一个事件内核对象句柄。用来接收IO完成通知的4种方法(后面会有介绍)的最后一种:使用IO完成端口会使用到这个成员。
---Internal成员用来保存已处理的IO请求的错误码。一旦我们发出一个异步IO请求,设备驱动程序会立即将Internal设为STATUS_PENDING,表示没有错误,因为操作尚未开始。通过检查此值我们可以使用
HasOverlappedIoCompeleted宏检查异步IO请求是否已经完成。
该宏定义为:
#define HasOverlappedIoCompeleted(pOverlapped)