同步设备I/O与异步设备I/O
异步设备I/O基础
以异步的方式来访问设备,必须先调用CreateFile,并在dwFlagsAndAttributes参数中指定FILE_FLAG_OVERLAPPED标志来打开设备。这个标志告诉系统我们想要以异步的方式来访问设备。
为了将I/O请求加入设备驱动程序的队列中,必须使用ReadFile和WriteFile函数.
BOOL ReadFile( HANDLE hFile, PVOID pvBuffer, DWORD nNumBytesToRead, PDWORD pdwNumBytes, OVERLAPPED* pOverlapped ); BOOL WriteFile( HANDLE hFile, CONST VOID *pvBuffer DWORD nNumBytesToWrite, PWORD pdwNumBytes, OVERLAPPED* pOverlapped );
当我们使用这两个函数中的任何一个时函数会检查hFile参数标志的设备是否是用FILE_FLAG_OVERLAPPED标志打开的。如果打开设备时指定了这个标志,那么函数会执行异步设备I/O。
OVERLAPPED结构
定义:
typedef struct _OVERLAPPED{ DWORD Internal; //[out] Error code DWORD InternalHigh; //[out] Number of bytes transferred DWORD Offset; //[in] Low 32-bit file offset DWORD OffsetHigh; //[in] High 32-bit file offset HANDLE hEvent;//[in] Event handle or data }
后三个成员必须在调用ReadFile和WriteFile之前进行初始化。另外两个成员由驱动程序设置。
Offset和 OffsetHigh
这两个成员构成一个64位的偏移量,它表示当访问文件的时候应该从哪里开始进行I/O操作。
在执行异步I/O的时候,系统会忽略文件指针。
hEvent成员
用来接收I/O完成通知的4中方法中,其中一种方法会用到这个成员。当使用可提醒I/O通知函数时,可以根据需要来使用这个成员。
Internal成员
这个成员用来保存已处理的I/O请求的错误码。一旦发出一个异步I/O请求,设备驱动程序会立即将Internal设为STATUS_PENDING,表示没有错误,因为操作尚未开始。
WinBase.h中定义的HasOverlappedIoCompleted宏允许我们检查一个异步I/O操作是否已经完成。如果请求还在等待状态,那么该宏会返回FALSE。如果I/O请求已经完成,那么该宏会返回TRUE。
#define HasOverlappedIoCompleted(pOverlapped) \
((pOverlapped)->internal != STATUS_PENDING)
InternalHigh成员
当异步I/O请求完成时,这个成员用来保存已传输的字节数。
异步设备I/O的注意事项
1.设备驱动程序不必以先入先出的方式来处理队列中的I/O请求。如果不按顺序来执行I/O请求能够提高性能,那么设备驱动程序一般都会这样做;
2,驱动程序总是会以同步的方式来执行某些操作,如NTFS文件的压缩、增大文件的长度、向文件追加信息等。要想进一步了解那些始终都以同步方式执行的操作,参阅http://support.microsoft.com/default.aspx?scid=kb%3Ben-us%3B156932。
3.在异步I/O请求完成之前,一定不能移动或是销毁在发出I/O请求时所使用的数据缓存和OVERLAPPED结构。
一个错误的例子:
VOID ReadData(HANDLE hFile){
OVERLAPPED = {0};
BYTE b[100];
ReadFile(hFile, b, 100, NULL,&o);
}
4.必须为每个I/O请求分配并初始化一个不同的OVERLAPPED结构。
取消队列中的设备I/O请求
1.调用CancelIo来取消由给定句柄所标志的线程添加到队列中的所有I/O请求(除非该句柄具有与之相关联的I/O完成端口):
BOOL CancelIo(HANDLE hFile);
2.关闭设备句柄,来取消已经添加到队列中的所有I/O请求,而不管它们是由哪个线程添加的。
3.当线程终止时,系统会自动取消该线程发出的所有I/O请求,但如果请求被发往的设备句柄具有与之相关联的I/O完成端口,那么它们不在被取消之列;
4.将发往给定文件句柄的一个指定的I/O请求取消:
BOOL CancelIoEx(HANDLE hFile,LPOVERLAPPED pOverlapped);
使用CancelIoEx,我们能够将调用线程之外的其它线程发出的待处理的I/O请求取消。这个函数会将hFile设备的待处理的I/O请求中所有与pOverlapped参数相关联的请求请求都标记为已取消。如果pOverlapped为NULL,那么CancelIoEx会将hFile指定的设备的所有待处理I/O请求都取消。