当前位置: 首页 > news >正文

如何使用Windows OVERLAPPED优化你的应用

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 codeDWORD InternalHigh; // [out] Number of bytes transferredDWORD Offset; // [in] Low 32-bit file offsetDWORD OffsetHigh; // [in] High 32-bit file offsetHANDLE 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里的InfomationLPOVERLAPPED 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_OVERLAPPEDNULL );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);//做一些其他操作,这些操作会与读设备并行执行//进入alterableSleepEx(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

http://www.jsqmd.com/news/1249175/

相关文章:

  • 2026北京名包“非专柜渠道”回收警示:代购包、奥莱款、折扣村货,回收时被区别对待吗? - 日常财经早知道
  • C++性能分析
  • TM4C129LNCZAD低功耗模式实战:从架构解析到代码实现
  • TM4C129 UART模块深度解析:从寄存器配置到DMA与中断优化
  • 生态修复理念下广州沙坑涌大源段河道景观提升设计
  • 计算机Django毕设实战-基于Django 的物流物资配送信息管理系统 企业物资出入库与配送调度系统设计【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 华为网络计算机ct2000,第八集[达特分享]华为CT5000一键刷BIOS安装OpenWRT制作软路由...
  • C++高并发内存池:三层架构设计与性能优化实践
  • AI驱动的电商运营革命(2024黑盒策略首次公开):头部品牌已用它悄悄跑赢92%同行
  • TMS320C5000 DMA高级应用实战:数据排序、程序分页与乒乓缓冲详解
  • double浮点数计算的一些问题,以及bigDecimal的使用,和DecimalFormat的对比、Long
  • 2026年实测:宁波4大周末语文小升初机构综合评测
  • Tiva™ TM4C129 EPI配置寄存器深度解析:从通用模式到主机总线时序优化
  • 2026扬州卫生间渗水发霉最全解答!不砸砖防水靠谱吗?根治楼下渗水方法 - 宅安选房屋修缮
  • Elastic 与 Deductive AI 携手合作,加速工程团队的 agent 式事件调查
  • 杭州方品科技深耕互联网技术赛道为产业数字化发展注入全新动能 - 资讯纵览
  • mvc 默认路由没有生效_许迎果 第172期 蜗牛星际U盘安装openwrt软路由全流程演示...
  • 向量数据库实战:选型、调优与落地~系列文章16:Milvus 分布式部署实战
  • C/C++输入输出深度解析:从缓冲区到文件操作,掌握I/O核心机制
  • 制造业中小工厂CRM选型指南与实施策略
  • 【毕业设计】基于 Django 的校园学生综合素质考核与评优系统 学生德育智育文体综合评分管理平台(源码+文档+远程调试,全bao定制等)
  • 深度解析TM4C129LNCZAD:从数据手册修订看工业MCU实战应用
  • AI招聘解决方案:重构招聘价值链的16项核心技术
  • TM4C129 μDMA实战:从原理到配置,解放CPU的数据搬运引擎
  • 深圳读书会私域管理软件选型指南:聚焦功能适配与实施边界
  • 2026青岛排气扇厂家哪家好,换气扇厂家推荐避坑指南:4个挑选要点,帮你绕开90%的坑 - mobible
  • 工业厂房机电装修工程怎么选?哪家靠谱服务水平更稳定 - 品牌深度评测
  • ARM DAP与JTAG指令深度解析:从调试原理到故障排查实战
  • TM4C129 UART寄存器深度解析:从波特率计算到DMA配置实战
  • JTAG接口原理与实战:从状态机到ARM Cortex-M调试全解析