同步IO是指:线程在发起IO请求后会被挂起,IO完成后继续执行。

  异步IO是指:线程发起IO请求后并不会挂起而是继续执行。IO完毕后会得到设备驱动程序的通知。

  一.异步准备与OVERLAPPED结构

  (1).为了以异步的方式来访问设备,必须先调用CreateFile,并在dwFlagsAndAttributes参数中指定FILE_FLAG_OVERLAPPED标志来打开设备。该标志告诉系统要以异步的方式来访问设备。

  为了将I/O请求加入设备驱动程序的队列中,必须使用ReadFile和WriteFile函数:

  

  1. HANDLE CreateFile(
  2. LPCTSTR lpFileName, // 文件名/设备路径 设备的名称
  3. DWORD dwDesiredAccess, // 访问方式
  4. DWORD dwShareMode, // 共享方式
  5. LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全描述符指针
  6. DWORD dwCreationDisposition, // 创建方式
  7. DWORD dwFlagsAndAttributes, // 文件属性及标志
  8. HANDLE hTemplateFile // 模板文件的句柄
  9. );

  当调用ReadFile,WriteFile这两个函数中任何一个时,函数会检查hFile参数标识的设备是否用FILE_FLAG_OVERLAPPED标志打开的。如果打开设备时指定了这个标志,那么函数会执行异步设备I/O。

  1. BOOL WINAPI ReadFile(
  2. _In_ HANDLE hFile,
  3. _Out_ LPVOID lpBuffer,
  4. _In_ DWORD nNumberOfBytesToRead,
  5. _Out_opt_ LPDWORD lpNumberOfBytesRead,
  6. _Inout_opt_ LPOVERLAPPED lpOverlapped
  7. );
  8.  
  9. BOOL WINAPI ReadFile(
  10. _In_ HANDLE hFile,
  11. _Out_ LPVOID lpBuffer,
  12. _In_ DWORD nNumberOfBytesToRead,
  13. _Out_opt_ LPDWORD lpNumberOfBytesRead,
  14. _Inout_opt_ LPOVERLAPPED lpOverlapped
  15. );

  

  (2).再来看OVERLAPPED结构:

  

  1. typedef struct _OVERLAPPED {
  2. ULONG_PTR Internal;
  3. ULONG_PTR InternalHigh;
  4. union {
  5. struct {
  6. DWORD Offset;
  7. DWORD OffsetHigh;
  8. } DUMMYSTRUCTNAME;
  9. PVOID Pointer;
  10. } DUMMYUNIONNAME;
  11.  
  12. HANDLE hEvent;
  13. } OVERLAPPED, *LPOVERLAPPED;

  1.Internal成员:这个成员用来保存已处理的I/O请求的错误码.

     InternalHigh成员:当异步I/O请求完成的时候,这个成员用来保存已传输的字节数。

  2..Offset和OffsetHigh成员,构成一个64位的偏移量,它们表示当访问文件的时候应该从哪里开始进行I/O操作。每个文件内核对象都有一个与之相关联的文件指针。在执行异步I/O的时候,系统会忽略文件指针。这是为了避免在对同一个对象进行多个异步调用的时候出现混淆,所有异步I/O请求必须在OVERLAPPED结构中指定起始偏移量。非文件设备会忽略这两个参数,必须将其初始化为0,否则I/O请求会失败。

  (3.)异步设备IO注意事项

    1:异步IO不会按照你的投递顺序来执行,驱动会选择他认为最快的方式来组合这些投递

    2:错误处理,以文件IO为例,当我们投递一个异步ReadFile()时,设备驱动程序可能会以同步方式执行,例如如果设备驱动程序发现要读取的数据在文件缓冲里时,就不会投递这个异步设备IO,而是直接将数据复制进我们的缓冲区

    3.如果IO是同步方式执行,ReadFile()和WriteFile()返回非零值,如果是异步或者出现错误,返回FALSE,调用GetLastError()获得错误码,如果返回的是ERROR_IO_PENDING,那么IO请求已经被成功地加入了队列。

  二.接收IO请求完成的方法  

Windows提供了4种不同的技术方法来得到I/O完成的通知。

技术 概要
通知一个设备内核对象 当一个设备同时有多个IO请求的时候,该方法不适用。允许一个线程发送一个IO请求,另一个线程处理之。
通知一个事件内核对象 允许一个设备同时有多个IO请求。允许一个线程发送一个IO请求,另一线程处理之。
警告IO 允许一个设备同时有多个IO请求。必须在同一个线程中发送并处理同一个IO请求。
IO完成端口 允许一个设备同时有多个IO请求。允许一个线程发送一个IO请求,另一个线程处理之。该方法伸缩性好,而且性能高。

  

  1.触发设备内核对象

  (1)Read/WriteFile在将I/O请求添加到队列之前,会先将对象设为未触发状态。当设备驱动程序完成了请求之后,会将设备内核对象设为触发状态,线程可以通过WaitForSingalObject或WaitForMultiObject来检查一个异步IO请求是否完成。

  (2)不能向同一个设备发出多个IO请求。

  2.触发事件内核对象

  (1)在每个I/O请求的OVERLAPPED结构体的hEvent创建一个用来监听该请求完成的事件对象。当一个异步I/O请求完成时,设备驱动程序会调用SetEvent来触发事件。驱动程序仍然会像从前一样,将设备对象也设为触发状态,因为己经有了可用的事件对象,所以可以通过SetFileCompletionNoticationModes(hFile,FILE_SKIP_SET_EVENT_ON_HANDLE)来告诉操作系统在操作完成时,不要触发文件对象。

  3.使用可提醒IO 

  (1)创建线程时,会同时创建一个与线程相关联的APC队列(异步过程调用),可以告诉设备程序驱动程序在I/O完成时,为了将通知信息添加到线程的APC队列中,需要调用ReadFileEx和WriteFileEx函数。

  (2)ReadFile/WriteFileEx函数与Read/WriteFile最大的不同在于最后一个参数,这是一个回调函数(也叫完成函数)的地址,当*Ex发出一个I/O请求时,这两个函数会将回调函数的地址传给设备驱动程序。当设备驱动程序完成I/O请求的时候,会在发出I/O请求的线程的APC队列中添加一项。该项包含了完成函数的地址以及发出I/O请求时使用的OVERLAPPED的地址。

  对于这种异步设备I/O方式,确实没什么用。重要的是微软为可提醒IO构建的基础设施——APC(Asynchronous Procedure Call),异步过程调用。

  当创建一个线程的时候,系统会为线程维护一个APC队列,该队列中的项目想要得到执行,线程必须处于可提醒等待状态,即使用SleepEx、WaitForSingleObjectEx、WaitForMultipleObjectsEx等函数,并且把最后一个参数设置为TRUE。当线程处于可提醒等待状态时,线程就会执行APC队列中的APC,之后执行过的APC就会清除队列,再进行下一次执行APC(如果APC队列中还有未执行的APC)。

  在这一小节中,作者的用意就是不要使用可提醒I/O方式进行异步设备I/O——因为可提醒I/O的两大缺陷:回调函数的累赘和线程的无负载均衡机制。

  1. // Overlapped.cpp : 定义控制台应用程序的入口点。
  2.  
  3. #include "stdafx.h"
  4. #include <windows.h>
  5. #include <iostream>
  6. /*
  7. Internal成员:这个成员用来保存已处理的I/O请求的错误码.
  8. InternalHigh成员:当异步I/O请求完成的时候,这个成员用来保存已传输的字节数。
  9. 在当初设计OVERLAPPED结构的时候,Microsoft决定不公开Internal和InternalHigh成员(名副其实)。随着时间的推移,Microsoft认识到这些成员包含的信息会对开发人员有用,因此把它们公开了。但是,Microsoft没有改变这些成员的名字,这是因为操作系统的源代码频繁地用到它们,而Microsoft并不想为此修改源代码。
  10. */
  11. using namespace std;
  12.  
  13. #define PAGE_SIZE 0x1000
  14. void Sub_1(); //ReadFile 异步操作
  15. void Sub_2(); //ReadFileEx
  16. DWORD WINAPI Sub_1ThreadProcedure(LPVOID ParameterData);
  17. DWORD WINAPI Sub_2ThreadProcedure(LPVOID ParameterData);
  18. OVERLAPPED __Overlapped = { 0 };
  19. char __BufferData[4] = {0};
  20.  
  21. int main()
  22. {
  23.  
  24. //Sub_1(); //触发事件内核对象
  25. Sub_2(); //可提醒IO
  26. }
  27. void Sub_1()
  28. {
  29. BOOL IsOk = FALSE;
  30. DWORD ReturnLength = 0;
  31. HANDLE FileHandle = CreateFile(L"ReadMe.txt", GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE,
  32. NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
  33. if (FileHandle == INVALID_HANDLE_VALUE)
  34. {
  35. int LastError = GetLastError();
  36. goto Exit;
  37. }
  38. //当一个异步IO请求完成的时候,驱动程序检查OVERLAPPED结构的hEvent成员是否为NULL
  39. //如果hEvent不为NULL,那么驱动程序会调用SetEvent来触发事件,这时候就是使用事件对象来检查一个设备操作是否完成,而不是等待设备(文件)对象
  40. __Overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); //绝对要创建
  41. HANDLE ThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Sub_1ThreadProcedure,
  42. (LPVOID)FileHandle, 0, NULL);
  43.  
  44. if (__BufferData == NULL)
  45. {
  46. goto Exit;
  47. }
  48. IsOk = ReadFile(FileHandle, __BufferData, 4, &ReturnLength, &__Overlapped); //事件必须创建
  49. if (IsOk == FALSE)
  50. {
  51. int LastError = GetLastError();
  52.  
  53. if (LastError == ERROR_IO_PENDING)
  54. {
  55. //成功
  56. printf("ERROR_IO_PENDING\r\n"); //重叠I/O返回标志
  57. }
  58. }
  59. WaitForSingleObject(ThreadHandle, INFINITE);
  60. Exit:
  61. if (FileHandle != NULL)
  62. {
  63. CloseHandle(FileHandle);
  64. FileHandle = NULL;
  65. }
  66. printf("\r\n");
  67. return;
  68. }
  69. DWORD WINAPI Sub_1ThreadProcedure(LPVOID ParameterData)
  70. {
  71. HANDLE FileHandle = (HANDLE)ParameterData;
  72. BOOL IsOk = FALSE;
  73. DWORD ReturnLength = 0;
  74. while (1)
  75. {
  76. IsOk = WaitForSingleObject(__Overlapped.hEvent, INFINITE);
  77. IsOk -= WAIT_OBJECT_0;
  78. if (IsOk == 0)
  79. {
  80. IsOk = GetOverlappedResult(FileHandle, &__Overlapped, &ReturnLength, INFINITE);
  81.  
  82. if (IsOk==TRUE)
  83. {
  84. int i = 0;
  85. for (i = 0; i < ReturnLength; i++)
  86. {
  87. printf("%c", __BufferData[i]);
  88. }
  89. __Overlapped.Offset += ReturnLength;
  90. ReadFile(FileHandle, &__BufferData, 4, &ReturnLength, &__Overlapped);
  91. }
  92.  
  93. else
  94. {
  95. //数据完毕
  96. break;
  97. }
  98.  
  99. }
  100. else
  101. {
  102. return 0;
  103. }
  104. }
  105.  
  106. return 0;
  107. }
  108.  
  109. void Sub_2()
  110. {
  111. BOOL IsOk = FALSE;
  112. HANDLE FileHandle = CreateFile(L"ReadMe.txt", GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE,
  113. NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
  114. if (FileHandle == INVALID_HANDLE_VALUE)
  115. {
  116. int LastError = GetLastError();
  117. goto Exit;
  118. }
  119.  
  120. //__Overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); //不能提供该事件
  121. HANDLE ThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Sub_2ThreadProcedure,
  122. (LPVOID)FileHandle, 0, NULL);
  123.  
  124. if (__BufferData == NULL)
  125. {
  126. goto Exit;
  127. }
  128. IsOk = ReadFileEx(FileHandle, __BufferData, 4, &__Overlapped, NULL);
  129. if (IsOk == FALSE)
  130. {
  131. int LastError = GetLastError();
  132.  
  133. if (LastError == ERROR_IO_PENDING)
  134. {
  135. //成功
  136. printf("ERROR_IO_PENDING\r\n"); //重叠I/O返回标志
  137. }
  138. }
  139. WaitForSingleObject(ThreadHandle, INFINITE);
  140. Exit:
  141. if (FileHandle != NULL)
  142. {
  143. CloseHandle(FileHandle);
  144. FileHandle = NULL;
  145. }
  146. printf("\r\n");
  147. return;
  148. }
  149.  
  150. DWORD WINAPI Sub_2ThreadProcedure(LPVOID ParameterData)
  151. {
  152. HANDLE FileHandle = (HANDLE)ParameterData;
  153. DWORD ReturnLength = 0;
  154. BOOL IsOk = FALSE;
  155. while (1)
  156. {
  157. IsOk = GetOverlappedResult(FileHandle, &__Overlapped, &ReturnLength, TRUE);
  158. //当一个可提醒IO完成时,设备驱动程序不会试图去触发一个事件对象
  159. //IsOk = WaitForSingleObject(__Overlapped.hEvent, INFINITE);
  160. if (IsOk == TRUE)
  161. {
  162. int i = 0;
  163. for (i = 0; i < ReturnLength; i++)
  164. {
  165. printf("%c", __BufferData[i]);
  166. }
  167.  
  168. __Overlapped.Offset += ReturnLength;
  169. ReadFileEx(FileHandle, &__BufferData, 4, &__Overlapped, NULL);
  170. }
  171. else
  172. {
  173. return 0;
  174. }
  175. }
  176.  
  177. return 0;
  178. }

  

    

  1. // Overlapped.cpp : 定义控制台应用程序的入口点。
  2. //
  3.  
  4. #include "stdafx.h"
  5. #include <windows.h>
  6. #include <iostream>
  7. using namespace std;
  8. VOID CALLBACK CompletionRoutine(
  9. _In_ DWORD ErrorCode,
  10. _In_ DWORD ReturnLength,
  11. _Inout_ LPOVERLAPPED Overlapped);
  12. HANDLE __FileHandle = NULL;
  13. char __BufferData[20] = {0};
  14. int main()
  15. {
  16. BOOL IsOk = FALSE;
  17. OVERLAPPED Overlapped = { 0 };
  18. __FileHandle = CreateFile(L"ReadMe.txt", GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE,
  19. NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, NULL);
  20. if (__FileHandle == INVALID_HANDLE_VALUE)
  21. {
  22. int LastError = GetLastError();
  23. goto Exit;
  24. }
  25. IsOk = ReadFileEx(__FileHandle, __BufferData, 4,&Overlapped,
  26. (LPOVERLAPPED_COMPLETION_ROUTINE)CompletionRoutine);
  27. if (IsOk == FALSE)
  28. {
  29. int LastError = GetLastError();
  30. if (LastError == ERROR_IO_PENDING)
  31. {
  32. //成功
  33. }
  34. }
  35. Exit:
  36. SleepEx(0,TRUE);
  37. if (__FileHandle != NULL)
  38. {
  39. CloseHandle(__FileHandle);
  40. __FileHandle = NULL;
  41. }
  42. printf("Input AnyKey To Exit\r\n");
  43. getchar();
  44. return 0;
  45. }
  46. VOID CALLBACK CompletionRoutine(
  47. _In_ DWORD ErrorCode,
  48. _In_ DWORD ReturnLength,
  49. _Inout_ LPOVERLAPPED Overlapped
  50. )
  51. {
  52. if (ErrorCode == ERROR_SUCCESS)
  53. {
  54. int i = 0;
  55. for (i = 0; i < ReturnLength; i++)
  56. {
  57. printf("%c", __BufferData[i]);
  58. }
  59.  
  60. Overlapped->Offset += ReturnLength;
  61. ReadFileEx(__FileHandle, __BufferData, 4, Overlapped,
  62. (LPOVERLAPPED_COMPLETION_ROUTINE)CompletionRoutine);
  63. }
  64. else if (ErrorCode==ERROR_HANDLE_EOF)
  65. {
  66. //数据完成
  67. printf("\r\n");
  68. }
  69. else
  70. {
  71.  
  72. }
  73. }

  

异步设备IO OVERLAPPED结构(设备内核对象 事件内核对象 可提醒IO)的更多相关文章

  1. 异步IO模型和Overlapped结构

    .NET中的 Overlapped 类 异步IO模型和Overlapped结构(http://blog.itpub.net/25897606/viewspace-705867/) 数据结构 OVERL ...

  2. 《Windows核心编程系列》九谈谈同步设备IO与异步设备IO之同步设备IO

    同步设备IO 所谓同步IO是指线程在发起IO请求后会被挂起,IO完成后继续执行. 异步IO是指:线程发起IO请求后并不会挂起而是继续执行.IO完毕后会得到设备的通知.而IO完成端口就是实现这种通知的很 ...

  3. 【RT-Thread笔记】IO设备模型及GPIO设备

    RTT内核对象--设备 RT-Thread有多种内核对象,其中设备device就是其中一种. 内核继承关系图如下: 设备继承关系图如下: device对象对应的结构体如下: 其中,设备类型type有如 ...

  4. Linux字符设备驱动基本结构

    1.Linux字符设备驱动的基本结构 Linux系统下具有三种设备,分别是字符设备.块设备和网络设备,Linux下的字符设备是指只能一个字节一个字节读写的设备,不能随机读取设备内存中某一数据,读取数据 ...

  5. linux块设备驱动---相关结构体(转)

    上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...

  6. 设备文件三大结构:inode,file,file_operations

    驱动程序就是向下控制硬件,向上提供接口,这里的向上提供的接口最终对应到应用层有三种方式:设备文件,/proc,/sys,其中最常用的就是使用设备文件,而Linux设备中用的最多的就是字符设备,本文就以 ...

  7. Linux设备文件三大结构:inode,file,file_operations

    驱动程序就是向下控制硬件,向上提供接口,这里的向上提供的接口最终对应到应用层有三种方式:设备文件,/proc,/sys,其中最常用的就是使用设备文件,而Linux设备中用的最多的就是字符设备,本文就以 ...

  8. 【Linux开发】linux设备驱动归纳总结(一):内核的相关基础概念

    linux设备驱动归纳总结(一):内核的相关基础概念 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  9. Linux设备驱动开发详解-Note(5)---Linux 内核及内核编程(1)

    Linux 内核及内核编程(1) 成于坚持,败于止步 Linux 2.6 内核的特点 Linux 2.6 相对于 Linux 2.4 有相当大的改进,主要体现在如下几个方面. 1.新的调度器 2.6 ...

随机推荐

  1. learn python the hard way 习题18~25总结

    定义函数和调用函数的语法 定义函数 形式: def functionName(p1,p2): statement other statement 需要注意: 紧跟者函数定义的代码是否使用了4个空格的缩 ...

  2. Fiddler抓包配置具体步骤

    如何查看手机连接的无线wifi的IP? 打开手机,选择设置->进入设置页面选择WLAN->进入WLAN管理,点击手机已经连接的路由器->点击进入查看,即可看见IP地址 如何查看自己电 ...

  3. day11-15,装饰器

    day11 1.装饰器 import time # print(time.time()) # 点数前边是从1970年到现在过了多少秒 # time.sleep(10) # 让程序执行到这里停一会儿 # ...

  4. cmd下可以启动java,输入javac提示 不“存在”

    方法:手动把JDK安装目录的bin目录配置到PATH环境变量里

  5. hive的jdbc使用

    ①新建maven项目,加载依赖包  在pom.xml中添加 <dependency> <groupId>jdk.tools</groupId> <artifa ...

  6. oracle的 表、 procedure、package等对象被锁,处理方法

    1.0  oracle中表被锁,处理方法 select t4.object_name, t3.spid, t1.oracle_username, t1.os_user_name  from v$pro ...

  7. Leetcode 150

    class Solution { public: int evalRPN(vector<string>& tokens) { stack<int> st; ;i < ...

  8. lua 函数基础

    函数定义在前,调用在后 如果函数只有一个实参,并且此参数是一个字面字符串或者table构造式,那么可以省略() 例如 print "hello" unpack{1,2} print ...

  9. oracle和sql server中,取前10条数据语法的区别

    在sql server中,取数据中前10条语句,我们可以用top 10 这样语句,但是oracle就没有这个函数,接下来介绍它们之间的区别 1.sql server 取前10语句和随机10条的语法 - ...

  10. python+ajaxFileUpload 无刷新上传文件

    需要准备文件 http://pan.baidu.com/s/1bp4N3nL   qqi0 html <script src="{% static 'js/jquery.js' %}& ...