您的位置:首页 > 其它

Windows异步IO

2014-07-17 08:50 423 查看
1、异步IO说明

同步IO时,发出IO请求的线程会被挂起。而异步IO时发出请求的线程不会被挂起,而是可以继续执行。异步IO请求传给了设备驱动程序,被加入到驱动程序的请求队列中,驱动程序负责实际的IO操作。当设备驱动程序完成了对队列中IO请求的处理,无论成功与否都必须通知应用程序。

为了以异步的方式来访问设备,在调用CreateFile()时,必须传给dwFlagsAndAttributes参数FILE_FLAG_OVERLAPPED标志,这个标志告诉系统我们想要以异步的方式来访问设备;ReadFile()和WriteFile()
的pdwNumBytes参数可以传NULL,因为函数会立即返回,检查pdwNumBytes是没有意义的;ReadFile()和WriteFile()的pOverlapped参数必须传入一个已初始化的OVERLAPPED结构,其中Offset、OffsetHigh、hEvent必须在调用ReadFile()和WriteFile()前初始化,Internal和InternalHigh由驱动程序设置。

typedef struct _OVERLAPPED  
{  
   DWORD Internal;     //错误代码。  
   DWORD InternalHigh; //已传输字节数。   
   DWORD Offset;       //低32位文件偏移。   
   DWORD OffsetHigh;   //高32位文件偏移。  
   HANDLE hEvent;      //事件对象句柄。   
}OVERLAPPED,*LPOVERLAPPED;
Internal:用来保存已处理的IO请求的错误码。一旦我们发出一个异步IO请求,设备驱动程序会立即将Internal设为STATUS_PENDING,表示没有错误。通过检查此值我们可以使用HasOverlappedIoCompeleted宏检查异步IO请求是否已经完成。该宏定义为:#define
HasOverlappedIoCompeleted(pOverlapped)\

((pOverlapped)->Internal!=STATUS_PENDING)

InternalHigh:保存已传输的字节数。

Offset、OffsetHigh:Offset和OffsetHigh共同构成一个64位的偏移量。它表示在访问文件时应该从何处访问设备。非文件设备必须为这两个成员都指定为0,否则IO请求将会失败。GetLastError返回ERROR_INVALID_PARAMETER。


之所以会在此处提供偏移量是因为:在执行异步操作时系统会忽略文件指针。如果不再此处指定,那么系统将无法知道下次应该从哪里开始读取数据。为了防止在同一个对象上进行多个异步调用时出现混淆,所有的异步调用IO请求都必须在OVERLAPPED结构中指定起始偏移量。

hEvent:标识一个事件内核对象句柄。用来接收IO完成通知的4种方法(后面会有介绍)的最后一种:使用IO完成端口会使用到这个成员。

2、异步IO注意事项

一:设备驱动程序队列的异步设备IO请求不一定是以先入先出方式处理的。后被加入的请求也有可能先执行。

二:如果请求的IO操作是以同步方式执行的,那么ReadFile和WriteFile会返回非零值。如果请求的IO操作是以异步方式执行的或者在调用函数时出现错误,函数会返回false。必须调用GetLastError来检查到底发生了什么事。如果GetLastError 返回ERROR_IO_PENDING,那么IO请求已经被就加入到了队列。其他值则说明IO请求没有被加入到驱动程序队列中。此时GetLastError会返回一下几个错误码:ERROR_INVALID_USER_BUFFER或ERROR_NOT_ENOUGH_MEMORY.这两个错误码表示驱动程序请求队列已满,无法添加。

三:我们以异步IO方式将IO请求添加到驱动程序队列中时,驱动程序会选择以同步的方式来处理请求。当我们从文件中读取数据时,系统会检查我们想要的数据是否在系统缓存中。如果数据已经在缓存中,系统就不会将我们的异步IO请求添加到设备驱动程序队列中。

四:在异步IO请求完成之前,一定不能移动或是销毁发出IO请求所使用的数据缓存和OVERLAPPED数据。由于传给CreateFile和WriteFile的是OVERLAPPED结构的地址,当系统将IO请求加入设备驱动程序会将地址传给驱动程序,否则将会导致内存访问违规。如以下代码中, 由于OVERLAPPED和buff是从栈中分配的,函数执行完毕栈被平衡,但异步IO请求可能还未执行完毕。此时再访问栈空间很容易导致访问违规。为避免这种情况的可以从堆中分配内存。

VOID ReadData(HANDLE hFile)

{

OVERLAPPED o={0};

BYTE buff[100];

ReadFile(hFile,buff,100,NULL,&o);

}

3、取消IO请求

有时候我们想要在设备驱动程序对一个已经加入加入队列的设备IO请求进行处理之前将其取消,Windows为我们提供了多种方式:

一:调用CancelIo()。该函数取消对该文件句柄的所有等待的I/O操作。也可以关闭设备句柄,来取消所有已经添
加到队列中的所有IO请求。

BOOL CancelIo(HANDLE hFile);

二:线程终止时,系统会自动取消该线程发出的所有IO请求。但如果请求的句柄具有与之相关联的IO完成端口,那么不在被取消之列。

三:CancelIoEx能够取消给定文件句柄的一个指定IO请求。它会将hFile设备待处理的IO请求中所有与pOverlapped相关联的请求都标记为已经取消。由于每个待处理的IO请求都应该有自己的OVERLAPPED结构,因此每次调用CancelIoEx只取消一个待处理的请求。如果pOverlapped为NULL,那么CancelIoEx会将hFile指定的设备的所有待处理IO请求都取消掉。被取消的IO请求会返回错误码ERROR_OPERATION_ABORTED。

BOOL CancelIoEx(HANDLE hFile,LPOVERLAPPED pOverlapped);

4、接收异步IO请求已完成通知

现在我们已经知道如何将异步设备IO添加到驱动程序队列中,但是我们还没有介绍驱动程序如何通知我们IO请求已经完成。

Windows提供了4中方式来接收IO请求已经完成的通知:

一:触发设备内核对象。对向一个设备同时发出多个IO请求时,这种方法无用。

二:触发事件内核对象。

三:使用可提醒IO。

四:使用IO完成端口。

转载出处:http://blog.csdn.net/ithzhang/article/details/8316171

http://blog.csdn.net/ithzhang/article/details/8508161
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: