01.异步I/O基本原理

I/O即输入输出。在现代操作系统中,输入输出是计算机完整功能必不可少的一部分。处理器负责各种计算任务,然后通过各种输入输出设备与外界进行交互。常见的输入输出设备包括键盘、鼠标、显示器、硬盘、网络适配器接口等。有了硬件设备,在软件层面上,使得操作系统通过以一致的方式与设备驱动交互从而操控硬件设备。而应用程序通过统一的接口与系统内核进行交互。

在计算机程序执行过程中,设备I/O是最慢和最不可预测的操作之一。CPU在执行算术运算甚至绘制屏幕时的速度都要比从外设中读写数据或通过网络传输数据快得多。使用异步I/O可以使应用程序能够更好地使用资源,从而实现执行效率更高的应用程序。

当一个函数或者线程向设备发出I/O请求时,这个I/O请求被传递给设备驱动程序,由设备驱动程序完成实际的I/O操作任务。当设备驱动程序在读写I/O外设时需要等待设备的响应,发起I/O操作的函数或线程并不会立即返回,必须要等待I/O操作完成后才可以进行下一步操作。尤其在一些设备I/O操作较多的程序中,这种情况会严重拖慢整个应用的执行效率。如果使用异步I/O操作,发起IO操作的函数或线程会将数据放在指定的buffer中,并且立即返回继续执行下一步的操作,比较耗时的I/O操作会由操作系统完成。这样就可以实现比较高效的I/O操作,优化程序的执行效率。

当使用异步I/O操作时,在操作系统内核中由设备驱动程序完成I/O操作队列,操作系统通过消息通知应用程序数据已经发送、数据已经收到或者出现了读写错误。使用异步I/O是设计高性能、可伸缩应用程序的本质,这也是我们要重点讨论的内容。

在Windows中要使用异步的方式访问设备,需要首先通过调用CreateFile来打开设备或者创建文件,在文件的属性和标志位中设置 FILE_FLAG_OVERLAPPED 属性。FILE_FLAG_OVERLAPPED 通知Windows操作系统此设备或文件将以异步方式来操作。

BOOL ReadFile(   HANDLE      hFile,   PVOID       pvBuffer,   DWORD       nNumBytesToRead,   PDWORD      pdwNumBytes,   OVERLAPPED* pOverlapped);BOOL WriteFile(   HANDLE      hFile,   CONST VOID  *pvBuffer,   DWORD       nNumBytesToWrite,   PDWORD      pdwNumBytes,   OVERLAPPED* pOverlapped);

当调用这两个读写函数时,该函数首先检查hFile参数中是否打开了FILE_FLAG_OVERLAPPED 标志位。如果使用了OVERLAPPED的标志位,则该函数执行异步设备I/O。当调用异步I/O中的任意一个函数时,可以为pdwNumBytes参数传递NULL。因为这些函数在I/O请求完成之前返回,检查传输的字节数是没有意义的。

02.OVERLAPPED结构体成员变量介绍

在使用异步I/O时,必须通过pOverlapped参数将地址传递给初始化的OVERLAPPED结构体。在此上下文中,单词“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} OVERLAPPED, *LPOVERLAPPED;

此结构体中包含五个成员变量。其中三个成员变量Offset、OffsetHigh 以及hEvent必须在调用ReadFile或WriteFile之前初始化。其他两个成员Internal和InternalHigh由设备驱动程序设置。下面对这些成员变量进行更详细的解释:

当程序读写一个文件时,Offset标记了此文件读写位置的偏移量。当操作系统进行文件操作时从文件指针指定的位置开始访问文件。操作完成后,操作系统自动更新文件指针,以便下次继续操作。

注意,对于非文件的设备I/O,Offset和OffsetHigh成员变量必须初始化为0,否则I/O请求会返回失败,GetLastError将会返回ERROR_INVALID_PARAMETER的错误。

hEvent可用于创建接收I/O完成之后由操作系统产生的事件。

Internal成员变量用来保存处理过的I/O操作错误码。一旦发出异步I/O的操作请求,设备驱动程序就会将Internal设置为STATUS_PENDING,表示没有发生错误,因为操作还没有开始。如果请求仍然在等待,则返回FALSE,如果I/O请求完成,则返回TRUE。当初设计OVERLAPPED结构体时,微软决定不暴露Internal和InternalHigh成员(这一点从名字也可以看出来)。随着时间的推移,微软意识到这些成员中包含的信息对开发人员有用,所以在后来的设计中开发人员可以从外部访问这两个变量。但是,为了保证兼容性微软并没有改变此成员变量的名字,因为系统内核也依赖于这两个变量。

03.接收异步I/O操作完成产生的事件

接下来我们讨论在I/O请求完成后设备驱动程序如何通知应用程序。Windows提供了四种不同的接收I/O完成通知的方法。下面对这些方法进行简单的介绍。这些方法按照复杂度的顺序排列,从最容易理解和实现的(发送设备内核对象的信号)到最难理解和实现的(I/O完成端口)。

完成端口是四种接收I/O操作完成事件的最佳方法。通过学习这四种方法,就可以了解微软为什么要将完成端口加到Windows中,以及完成端口如何补充其他方法所存在的不足。

04.代码片段

下面代码演示如何在应用程序中使用ReadFileEx进行异步读操作​​​​​​​

#include <windows.h>#include <stdio.h>#define DEVICE_NAME    "test.dat"#define BUFFER_SIZE    512//假设该文件大于或等于BUFFER_SIZEVOID CALLBACK MyFileIOCompletionRoutine(  DWORD dwErrorCode,                  // 对于此次操作返回的状态  DWORD dwNumberOfBytesTransfered, // 告诉已经操作了多少字节,也就是在//IRP里的Infomation  LPOVERLAPPED lpOverlapped         // 这个数据结构){    printf("IO operation end!\n");}int main(){    HANDLE hDevice =         CreateFile("test.dat",                    GENERIC_READ | GENERIC_WRITE,                    0,                    NULL,                    OPEN_EXISTING,                    FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,//此处设置FILE_FLAG_OVERLAPPED                    NULL );    if (hDevice == INVALID_HANDLE_VALUE)     {        printf("Read Error\n");        return 1;    }    UCHAR buffer[BUFFER_SIZE];    //初始化overlap使其内部全部为零    //不用初始化事件!!    OVERLAPPED overlap={0};    //这里没有设置OVERLAP参数,因此是异步操作    ReadFileEx(hDevice, buffer, BUFFER_SIZE,&overlap,MyFileIOCompletionRoutine);    //做一些其他操作,这些操作会与读设备并行执行    //进入alterable    SleepEx(0,TRUE);    CloseHandle(hDevice);    return 0;}

05.进一步资料

要实现异步I/O操作可以直接参考Windows 官方源码来做测试demo,同时也可以参考一些优秀的软件的代码片段来实现Windows 异步I/O的实现。这里列出几个可以做代码参考的链接。

1.Windows 官方文档 Data Access and Storage

Data Access and Storage - Win32 apps | Microsoft Docs

2.tcp2com项目

tcp2com download | SourceForge.net

https://sourceforge.net/projects/tcp2com/

此项目用于实现TCP与串口数据互相转发,代码实现完整,具有较高的参考价值。

参考链接:

[1] ReadFile function

https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile?redirectedfrom=MSDN

[2] WriteFile function

https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile?redirectedfrom=MSDN

如何使用Windows OVERLAPPED优化你的应用相关推荐

  1. Advanced SystemCare Pro 中文绿色特别便携正式版一款易于使用的 Windows 电脑优化工具

    Advanced SystemCare Pro 中文绿色特别便携正式版 是 IObit 公司开发的一款易于使用的 Windows 电脑优化工具,是难得一见的高性能优化工具.它能对系统进行全面的诊断,在 ...

  2. 俄大神 lopatkin Windows 精简优化系统 - 工具软件

    昨天有个网友邮件我,说是想找个Tiny7 Rev2的ISO操作系统文件,但是我找了下,以前的那些文件有些已经删除了,所以就在网上搜到了俄大神 lopatkin Windows 精简优化系统,特此放到网 ...

  3. 分享一个windows 10优化用的注册表

    转摘于http://wuyou.net/forum.php?mod=viewthread&tid=413465 分享一个自己用的windows 10优化用的注册表,再也不用点来点去了... 开 ...

  4. 如何启动和退出w8ndows,Windows 8优化设置技巧,加快你的win8运行速度

    Windows 8优化设置技巧,加快你的win8运行速度 书法字体2013.03.19win8优化 收集的实用Windows 8优化技巧,告诉你如何加快你的win8运行速度.虽然已经有越来越多的用户已 ...

  5. [转载]windows内存优化 没你想像那么美

    打开搜索引擎,输入"内存优化软件",你一定可以找到数百种内存优化软件.在这么多选择之中,该如何选择呢?如果告诉你:正确答案是"不选择",你会不会很吃惊? 相比W ...

  6. 效率提升-windows空间优化工具spacesniffer

    背景 最近在家里办公,是windows系统,C盘空间快红了,现在优化一下.博主推荐使用spacesniffer工具来辅助. 实践 打开spacesniffer工具,直接进行扫描,可以看到使用空间被非常 ...

  7. Windows 7 优化(收集整理)

    从网上收集整理的  Windows7 优化设置 来源于网络,回馈于网络 开篇第一讲 要有良好的使用电脑的习惯 1.软件安装目录不要放在C盘 2.不重要的软件,在安装完后记得随手关掉自动升级 3.桌面不 ...

  8. Windows 10 优化方案

    [系统基本要求] Windows 10家庭版64bit操作系统 测试激活码(不保证永久有效):TX9XD-98N7V-6WMQ6-BX7FG-H8Q99 windows10家庭版放弃,使用window ...

  9. Windows安全优化小助手

    @echo off mode con: cols=80 lines=25 if exist "%tmp%/win.reg" del /a /f "%tmp%/win.re ...

最新文章

  1. java dom4 引入_java – 使用dom4j从节点获取属性值
  2. [Share].NET Reflector Add-Ins
  3. C# == 和equals()区别
  4. 求递推序列的第N项(51Nod-1126)
  5. python构造函数
  6. java下载不了怎么_如何下载和安装Java
  7. Recycler 病毒(jwgkvsq.vmx)手动查杀
  8. 关于如何查找和利用PCL库学习资源的一些心得
  9. oracle update命令未正确结束,ORA-00933: SQL 命令未正确结束处理办法
  10. 通过命令完成 虚拟机 ubuntu 中的文件拷贝到主机
  11. FastAPI 是什么?
  12. C++核心准则T.61:不要过度参数化成员(SCARY)
  13. 强化学习——策略学习
  14. Linux 下 ls -l 命令执行显示结果的每一列含义
  15. android常用api大全,Android相关常用API……
  16. Python处理CSV文件(一)
  17. iOS开发基础之第三方调起自己的App
  18. iOS 10.2 越狱更新到yalu102 beta7,很稳定,很多注意事项及修复ssh和scp连接教程
  19. 【Word】如何在数学公式同一行末尾填写编号
  20. linux脚本程序是什么意思,Linux中$?是什么意思?

热门文章

  1. python中的type函数-python的type函数
  2. 用栈实现算术表达式 java_java的栈和用栈来解析算术表达式
  3. java 秒表时间格式化_java方法计时器,懒汉式,转换为00:00:00格式
  4. ElasticSearch概述及安装
  5. linux c 日志写入文件,linux下C语言实现写日志功能
  6. c语言fread malloc,流操作之读写(fread、fwrite、fopen、malloc)
  7. java分享知识点_Java基础知识点整理(一)
  8. c++代码转为go_C++调用Go方法的字符串传递问题及解决方案
  9. qtableview点击行将整行数据传过去_掌握这15个可视化图表,小白也能轻松玩转数据分析...
  10. STM32F405 标准库 SHT20温湿度传感器