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

同步设备IO

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

异步IO是指:线程发起IO请求后并不会挂起而是继续执行。IO完毕后会得到设备的通知。而IO完成端口就是实现这种通知的很好的一种方式。

线程是我们开发高性能、响应性好的一个必不可少的工具。这样在多处理器上就可以同时执行多个操作,从而提高吞吐量。当线程发出一个同步设备IO请求的时候,它会被临时挂起,直到设备完成IO请求为止。但线程阻塞会损害性能,这里有个问题是我们如何让线程不被挂起。

让线程始终进行有用的工作就需要它们相互通信,鼎力配合。Windows经过数年的研究和测试,开发出了一种被称为IO完成端口的机制的技术。它可以帮助我们创建高性能而且伸缩性好的应用程序。通过使用完成端口我们可以让线程在读取设备和写入设备而不必等待设备的响应,从而显著的提高吞吐量。

作为Windows程序员都必须要完全理解IO完成端口的工作原理。

Windows支持多种不同种类的设备。在此,我们把设备定义为能够与之进行通信的任何东西。如文件、目录、串口、并口、套接字、控制台等。接下来讨论是与这些设备进行通信,此种方式下与线程通信时,线程需要挂起等待设备响应---这种方式被称为同步IO。

Windows向开发人员隐藏了各种设备的差异,许多WindowsAPI允许我们以相同的方式来从设备读取数据和向设备写入数据,而不必关心何种类型的设备。

CreateFile函数。

CreateFile当然可以创建和打开磁盘文件。但是不要被它的名字所迷惑。它同样可以打开其他设备。根据传入参数的不同可以让CreateFile打开不同的设备。

[cpp]  view plain copy
  1. HANDLE CreateFile(
  2. PCTSTR pszName,
  3. DWORD dwDesiredAccess,
  4. DWORD dwShareMode,
  5. PSECURITY_ATTRIBUTES psa,
  6. DWORD dwCreationDisposition,
  7. DWORD dwFlagsAndAttributes,
  8. HANDLE hFileTemplate);

psaName既表示设备类型也表示该类设备一个实例。

dwDesiredAccess用来指定我们以何种方式和设备通信。可以传入以下值:

0                           不允许读写,但可以改变设备属性。

GENERIC_READ                只读访问

GENERIC_WRITE               只写访问

GENERIC_READ|GENERIC_WRITE  读写访问。

dwSharedMode用来指定共享权限:

0                                独占对设备的访问。如果设备已经打开,我们    的CreateFile会失败。

FILE_SHARE_READ                  只读共享,不允许修改内容。如果设备已经以写入或独占方式打开,我们的CreateFile会失败。

FILE_SHARE_WRITE                 写共享,不允许读取内容。如果设备已经以读取或独占方式打开,我们的CreateFile会失败。

FILE_SHARE_READ|FILE_SHARE_WRITE  不关心向设备读还是写数据。如果设备已经以独占方式打开,我们的CreateFile会失败。

FIEL_SHARE_DELETE                 先将文件标记待删除,所有对该文件引用的句柄都关闭之后,才将其真正的删除。

psa指向一个PSECURITY_ATTRIBUTES结构,用来指定安全属性。只有当我们在具备安全性的文件系统中,如NTFS中创建文件时才会用到此结构。在其他情况下都只需要传入NULL就可以了,此时会用默认的安全属性来创建文件,并且返回的句柄是不可继承的。

dwCreationDisposition参数对文件的含义更重大。它可以是以下值:

CREATE_NEW        创建一个新文件。如果同名文件存在则失败。

CREATE_ALWAYS     文件同名文件存在与否都创建文件。存在时会覆盖。

OPEN_EXISTING     打开一个已存在文件。如不存在,则失败。

OPEN_ALWAYS        打开一个已存在文件。如不存在,则创建。

TRUNCATE_EXISTING 打开一个已存在文件,将文件大小截断为0,如果不存在则调用失败。

dwFlagsAndAttributes有两个用途:一,允许我们设置一些标志微调与设备的通信。二:如果设备是文件,还可以设置文件属性。这些标志大多数是一些信号,用来告诉系统我们打算以何种方式来访问设备,这样系统就可以对缓存算法进行优化。此处不再介绍。

hFileTemplate,既可以标识一个已经打开的文件句柄,也可以是NULL。如果是一个文件句柄,那么CreateFile会完全忽略dwFlagsAndAttributes参数,转而使用hFileTemplate标识的文件属性。此时,hFileTemplate标识的文件句柄必须是一个用GENERIC_READ标志打开的文件。

CreateFile成功的创建或打开设备那会返回设备句柄。否则返回INVALID_HANDLE_VALUE。一定要注意返回值不是NULL哦。

Windows在设计时使用了64位来表示文件大小。但是64位需要分两个32位值来传入。实际上在日常工作中还有使用大于4G的文件。高32位在大多数情况下都会是0。

GetFileSizeEx用于得到文件大小。

[cpp]  view plain copy
  1. BOOL GetFileSizeEx(
  2.    HANDLE hFile,
  3. PLARGE_INTEGER pliFileSize);

hFile表示一个一打开文件的句柄。

pliFileSize表示文件大小。定义如下:

[cpp]  view plain copy
  1. typedef union _LARGE_INTEGER
  2. {
  3. struct
  4. {
  5. DWORD LowPart;
  6. LONG HighPart;
  7. };
  8. LONGLONG QuadPart;
  9. }LARGE_INTEGER,*PLARGE_INTEGER;

它允许我们以一个64位有符号数或者是两个32位值来表示一个64位数。

另外一个很重要的函数是GetCompressedFileSize:

[cpp]  view plain copy
  1. DWORD GetCompressedFileSize(
  2. PCTSTR pszFileName,
  3. PDWORD pdwFileSizeHigh);

这个函数返回文件物理大小,而GetFileSizeEx是返回文件逻辑大小。

CreateFile会创建一个文件内核对象来管理文件。返回的句柄就是对该文件内核对象的引用。在这个内核对象中有一个文件指针,它表示应该在哪里执行下一次读取或写入操作。开始时它的值是0。

SetFilePointerEx可以通过操作文件指针实现随机访问文件。

[cpp]  view plain copy
  1. BOOL SetFilePointerEx(
  2. HANDLE hFile,
  3. LARGE_INTEGER liDistanceToMove,
  4. PLARGE_INTEGER pliNewFilePointer,
  5. DWORD dwMoveMethod);

hFile表示我们要操作的文件指针。

liDistanceToMove指定我们要移动文件指针的字节数。系统会把我们指定的值与文件指针的当前值相加。传入负值是合法的。

dwMoveMethod指定移动文件指针的起始位置。

FILE_BEGIN      从文件开头开始。

FILE_CURRENT   从当前位置开始。

FILE_END       从文件末尾。

pliNewFilePointer返回文件指针的新值。

设置文件尾。

[cpp]  view plain copy
  1. BOOL SetEndOfFile(HANDLE hFile);

此函数会根据文件对象当前的文件指针当前所在的位置,截断文件。如果想要将文件设置为2k,可以这样:

[cpp]  view plain copy
  1. LARGE_INTEGER li;
  2. li.QuadPart=2048;
  3. SetFilePointerEx(hFile,li,NULL,FILE_BEGIN);
  4. SetEndOfFile(hFile);
  5. CloseHandle(hFile);

ReadFile和WriteFile

[cpp]  view plain copy
  1. BOOL ReadFile(
  2. HANDLE hFile,
  3. PVOID pvBuffer,
  4. DWORD nNumBytesToRead,
  5. PDWORD pdwNumBytes,
  6. OVERLAPPED*pOverlapped);
  7. BOOL WriteFile(
  8. HANDLE hFile,
  9. CONST VOID *pvBuffer,
  10. DWORD nNumBytesToWrite,
  11. PDWORD pdwNumbytes,
  12. OVERLAPPED*pOverlapped);

hFile表示我们要访问的设备。调用CreateFile打开设备时一定不能指定FILE_FLAG_OVERLAPPED标志,否则系统认为我们想要与该设备执行异步IO。

pvBuffer指向一个缓存,函数会把设备数据读取到该缓存中或者把该缓存的数据写入设备。

nNumbytesToRead和nNumBytesToWrite分别告诉ReadFile和WriteFile要从设备读取或写入多少字节。

pdwNumBytes返回读取的字节或向设备写入的字节。

在执行同步IO时,最后一个参数pOverlapped应该被设为NULL。

ReadFile和WriteFile执行成功后都返回true。

FlushBuffers将数据刷新至设备。

[cpp]  view plain copy
  1. BOOL FlushFileBuffers(HANDLE hFile);

该函数会强制将hFile参数所标识的设备相关联的所有缓存写入设备。

同步IO很容易使用,但是它会阻塞线程。比如:如果由于CreateFile正在执行同步IO操作而导致线程被阻塞,那么该线程的其他操作都会得不到处理。更严重的情况是会导致应用程序停止响应。Windows允许我们取消指定线程尚未完成的同步IO请求。

[cpp]  view plain copy
  1. BOOL CancelSynchronousIo(HANDLE hThread);

hThread标识由于等待同步IO完成而被挂起的线程句柄。这个句柄必须是使用THREAD_TERMINATE访问权限创建的。否则,函数会失败。GetLastError会返回ERROR_ACCESSS_DENIED。

我们自己创建的线程的安全属性是THREAD_ACCESS的,其中包括THREAD_TERMINATE。如果我们利用线程池,那么我们必须调用OpenThread来得到当前线程标识符对应的线程句柄,同时传入THREAD_TERMINATE。

CanelSynchronousIo会将等待IO完成而被挂起的线程唤醒。如果线程并不是因为要等待设备响应而被挂起,函数返回false。GetLastError返回ERROR_NOT_FOUND。

即便如此,为了创建响应性好的应用程序我们应该尽可能的执行异步IO操作。下一篇博文会有详细介绍。

posted @ 2018-05-18 09:11 史D芬周 阅读( ...) 评论( ...) 编辑 收藏

《Windows核心编程系列》九谈谈同步设备IO与异步设备IO之同步设备IO相关推荐

  1. 《windows核心编程系列》十八谈谈windows钩子

    windows应用程序是基于消息驱动的.各种应用程序对各种消息作出响应从而实现各种功能. windows钩子是windows消息处理机制的一个监视点,通过安装钩子能够达到监视指定窗体某种类型的消息的功 ...

  2. 《windows核心编程系列》二谈谈ANSI和Unicode字符集

    第二章:字符和字符串处理 使用vc编程时项目-->属性-->常规栏下我们可以设置项目字符集合,它可以是ANSI(多字节)字符集,也可以是unicode字符集.一般情况下说Unicode都是 ...

  3. Windows核心编程学习九:利用内核对象进行线程同步

    注:源码为学习<Windows核心编程>的一些尝试,非原创.若能有助于一二访客,幸甚. 1.程序框架 #include "Queue.h" #include <t ...

  4. [笔记]Windows核心编程《十九》DLL基础

    系列文章目录 [笔记]Windows核心编程<一>错误处理.字符编码 [笔记]Windows核心编程<二>内核对象 [笔记]Windows核心编程<三>进程 [笔记 ...

  5. [笔记]Windows核心编程《二十》DLL的高级操作技术

    系列文章目录 [笔记]Windows核心编程<一>错误处理.字符编码 [笔记]Windows核心编程<二>内核对象 [笔记]Windows核心编程<三>进程 [笔记 ...

  6. [笔记]Windows核心编程《十六》线程栈

    系列文章目录 [笔记]Windows核心编程<一>错误处理.字符编码 [笔记]Windows核心编程<二>内核对象 [笔记]Windows核心编程<三>进程 [笔记 ...

  7. [笔记]Windows核心编程《十三》windows内存体系结构

    系列文章目录 [笔记]Windows核心编程<一>错误处理.字符编码 [笔记]Windows核心编程<二>内核对象 [笔记]Windows核心编程<三>进程 [笔记 ...

  8. 线程与内核对象的同步——Windows核心编程学习手札之九

    线程与内核对象的同步 --Windows核心编程学习手札之九 用户方式下的线程同步机制具有速度快的特点,但有其局限性,对于许多应用程序来说,并不合适.例如,互锁函数家族只能在单值上运行,根本无法使线程 ...

  9. 内核对象——Windows核心编程学习手札系列之三

    内核对象 --Windows核心编程学习手札系列之三 内核对象可供系统和应用程序使用来管理各种各样的资源,如进程.线程.文件等,是内核分配的一个内存块,只能又内核访问,该内存块是一种数据结构,它的成员 ...

最新文章

  1. 查看anaconda所用python安装路径conda info --env
  2. ubuntu 终端常用命令
  3. 信息系统项目管理师-人力资源管理知识点
  4. 二年级的数学题,10个人有9个人做错,你能做对吗?
  5. 工作流技术杂谈 --- 2008
  6. poj 1716(贪心)
  7. html树状图右侧_如何在 Tableau 中创建控制图?
  8. 函数基本使用-第一个函数演练
  9. 用U盘或移动硬盘安装Windows7 (超简单制作Win7安装U盘方法)
  10. 大一计算机考试题库知识点,大一 计算机基础 期末考试题库
  11. MYSQL 4种插入数据的方式比较
  12. 深度学习-23:矩阵理论(L0/L1/L2范数)
  13. “十四五”融合地表水国控断面监测数据(约3641个点位,含水质等级、水温,pH,溶解氧,电导率,浊度,高猛酸盐指数,化学需氧量,五日生化需氧量,氨氮,总磷,总氮,铜,锌,氟化物,硒,砷,汞,镉,等)
  14. 多维向量空间中点到线的距离公式
  15. 5-1. 约分最简分式(15)
  16. 背包问题(最大装载价值)
  17. golang内幕之协程状态切换
  18. android 获取wifi的ip地址吗,Android开发实现在Wifi下获取本地IP地址的方法
  19. Itest(爱测试),最懂测试人的开源测试管理软件隆重发布
  20. FRP (高性能反向代理应用)

热门文章

  1. Maple中的部分特殊函数及其定义
  2. 培训日常分享--牛鑫宇老师2022年8月20-21号上海《会计学》MBA课程圆满结束!
  3. iphone 升级 出现 This device isn't eligible for the requested build 解决方法
  4. Android Chronometer控件
  5. 从“双卡双待“到”双通“,vivo率先推动DSDA架构落地
  6. 用Proxmox搞定gpu穿透(引用)
  7. 疯狂星期四;python摸鱼小游戏,重温童年经典
  8. Spring Cloud Alibaba微服务项目中集成Redis实现分布式事务锁实践
  9. 【EasyMouse】把你的安卓手机变成无线鼠标
  10. 程序员炫技:用c语言开发对对碰游戏,你打几分?