《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打开不同的设备。
- HANDLE CreateFile(
- PCTSTR pszName,
- DWORD dwDesiredAccess,
- DWORD dwShareMode,
- PSECURITY_ATTRIBUTES psa,
- DWORD dwCreationDisposition,
- DWORD dwFlagsAndAttributes,
- 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用于得到文件大小。
- BOOL GetFileSizeEx(
- HANDLE hFile,
- PLARGE_INTEGER pliFileSize);
hFile表示一个一打开文件的句柄。
pliFileSize表示文件大小。定义如下:
- typedef union _LARGE_INTEGER
- {
- struct
- {
- DWORD LowPart;
- LONG HighPart;
- };
- LONGLONG QuadPart;
- }LARGE_INTEGER,*PLARGE_INTEGER;
它允许我们以一个64位有符号数或者是两个32位值来表示一个64位数。
另外一个很重要的函数是GetCompressedFileSize:
- DWORD GetCompressedFileSize(
- PCTSTR pszFileName,
- PDWORD pdwFileSizeHigh);
这个函数返回文件物理大小,而GetFileSizeEx是返回文件逻辑大小。
CreateFile会创建一个文件内核对象来管理文件。返回的句柄就是对该文件内核对象的引用。在这个内核对象中有一个文件指针,它表示应该在哪里执行下一次读取或写入操作。开始时它的值是0。
SetFilePointerEx可以通过操作文件指针实现随机访问文件。
- BOOL SetFilePointerEx(
- HANDLE hFile,
- LARGE_INTEGER liDistanceToMove,
- PLARGE_INTEGER pliNewFilePointer,
- DWORD dwMoveMethod);
hFile表示我们要操作的文件指针。
liDistanceToMove指定我们要移动文件指针的字节数。系统会把我们指定的值与文件指针的当前值相加。传入负值是合法的。
dwMoveMethod指定移动文件指针的起始位置。
FILE_BEGIN 从文件开头开始。
FILE_CURRENT 从当前位置开始。
FILE_END 从文件末尾。
pliNewFilePointer返回文件指针的新值。
设置文件尾。
- BOOL SetEndOfFile(HANDLE hFile);
此函数会根据文件对象当前的文件指针当前所在的位置,截断文件。如果想要将文件设置为2k,可以这样:
- LARGE_INTEGER li;
- li.QuadPart=2048;
- SetFilePointerEx(hFile,li,NULL,FILE_BEGIN);
- SetEndOfFile(hFile);
- CloseHandle(hFile);
ReadFile和WriteFile
- 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表示我们要访问的设备。调用CreateFile打开设备时一定不能指定FILE_FLAG_OVERLAPPED标志,否则系统认为我们想要与该设备执行异步IO。
pvBuffer指向一个缓存,函数会把设备数据读取到该缓存中或者把该缓存的数据写入设备。
nNumbytesToRead和nNumBytesToWrite分别告诉ReadFile和WriteFile要从设备读取或写入多少字节。
pdwNumBytes返回读取的字节或向设备写入的字节。
在执行同步IO时,最后一个参数pOverlapped应该被设为NULL。
ReadFile和WriteFile执行成功后都返回true。
FlushBuffers将数据刷新至设备。
- BOOL FlushFileBuffers(HANDLE hFile);
该函数会强制将hFile参数所标识的设备相关联的所有缓存写入设备。
同步IO很容易使用,但是它会阻塞线程。比如:如果由于CreateFile正在执行同步IO操作而导致线程被阻塞,那么该线程的其他操作都会得不到处理。更严重的情况是会导致应用程序停止响应。Windows允许我们取消指定线程尚未完成的同步IO请求。
- 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操作。下一篇博文会有详细介绍。
《Windows核心编程系列》九谈谈同步设备IO与异步设备IO之同步设备IO的更多相关文章
- 《windows核心编程系列》十九谈谈使用远程线程来注入DLL。
windows内的各个进程有各自的地址空间.它们相互独立互不干扰保证了系统的安全性.但是windows也为调试器或是其他工具设计了一些函数,这些函数可以让一个进程对另一个进程进行操作.虽然他们是为调试 ...
- 《windows核心编程系列》十八谈谈windows钩子
windows应用程序是基于消息驱动的.各种应用程序对各种消息作出响应从而实现各种功能. windows钩子是windows消息处理机制的一个监视点,通过安装钩子能够达到监视指定窗体某种类型的消息的功 ...
- 《Windows核心编程系列》二十谈谈DLL高级技术
本篇文章将介绍DLL显式链接的过程和模块基地址重定位及模块绑定的技术. 第一种将DLL映射到进程地址空间的方式是直接在源代码中引用DLL中所包含的函数或是变量,DLL在程序运行后由加载程序隐式的载入, ...
- 《windows核心编程系列》十七谈谈dll
DLL全称dynamic linking library.即动态链接库.广泛应用与windows及其他系统中.因此对dll的深刻了解,对计算机软件开发专业人员来说非常重要. windows中所有API ...
- 《windows核心编程系列》十六谈谈内存映射文件
内存映射文件允许开发人员预订一块地址空间并为该区域调拨物理存储器,与虚拟内存不同的是,内存映射文件的物理存储器来自磁盘中的文件,而非系统的页交换文件.将文件映射到内存中后,我们就可以在内存中操作他们了 ...
- 《Windows核心编程系列》十四谈谈默认堆和自定义堆
堆 前面我们说过堆非常适合分配大量的小型数据.使用堆可以让程序员专心解决手头的问题,而不必理会分配粒度和页面边界之类的事情.因此堆是管理链表和数的最佳方式.但是堆进行内存分配和释放时的速度比其他方式都 ...
- 《windows核心编程系列》二谈谈ANSI和Unicode字符集 .
http://blog.csdn.net/ithzhang/article/details/7916732转载请注明出处!! 第二章:字符和字符串处理 使用vc编程时项目-->属性-->常 ...
- Windows核心编程学习九:利用内核对象进行线程同步
注:源码为学习<Windows核心编程>的一些尝试,非原创.若能有助于一二访客,幸甚. 1.程序框架 #include "Queue.h" #include <t ...
- 《windows核心编程系列》二十一谈谈基址重定位和模块绑定
每个DLL和可执行文件都有一个首选基地址.它表示该模块被映射到进程地址空间时最佳的内存地址.在构建可执行文件时,默认情况下链接器会将它的首选基地址设为0x400000.对于DLL来说,链接器会将它的首 ...
随机推荐
- ultraiso:usb-hdd+ v2
http://cn.ezbsystems.com/ultraiso/download.htm 普通隐藏:建立一个LBA前置的PBR包含表头的活动分区.高端隐藏:建立一个LBA后置的PBR包含表头的活动 ...
- Linux 命令 sudo
sudo 这个命令. 是为了 让 普通用户 ,也能够以root的身份来运行 操作, 而这些普通用户 又不须要知道root的password. 在 sudo 运行命令的时候. 仅仅须要 输入自己的pas ...
- 【独立开发人员er Cocos2d-x实战 008】BMFont生成位图字体工具和Cocos2dx使用载入fnt文件
1.首先我们须要下载而且安装BMFont工具,下载地址例如以下:http://download.csdn.net/detail/chenqiai0/8899353(里面还有具体的使用文档,假设使用中有 ...
- #include<> 和 #include""的区别
#include< file >编译程序会先到标准函数库中找文件 #include”file” 编译程序会先从当前目录中找文件 参考原文 转: 在C程序中包含文件有以下两种方法: (1)用 ...
- activiti自己定义流程之自己定义表单(一):环境配置
先补充说一下自己定义流程整个的思路,自己定义流程的目的就是为了让一套代码解决多种业务流程.比方请假单.报销单.採购单.协作单等等.用户自己来设计流程图. 这里要涉及到这样几个基本问题,一是不同的业务需 ...
- notepad++ 查找引用(Find Reference)(适用于c c++及各类脚本比方lua、python等)
在程序开发过程中,程序猿经经常使用到的一个功能就是查找引用(Find Reference).Visual Studio里面的相应功能是"查找全部引用"(Find All Refer ...
- Linux—read
read:将信息读入一个或多个Shell变量 语法格式:read [-r] 变量名 选项: -r:原始读入,不做任何处理,不将结尾结尾处的反斜杠解释为续行字符 行为模式 ...
- HLJU 1220: AC100天 (枚举)
1220: AC100天 Time Limit: 1 Sec Memory Limit: 128 MB Submit: 89 Solved: 12 [id=1220">Submit ...
- my-small.cnf my-medium.cnf my-large.cnf my-huge.cnf
my-small.cnf my-medium.cnf my-large.cnf my-huge.cnf 是 MySQL 默认的几个配置文件.针对不同配置的服务器可以使用不同的配置文件,将你需要的那一个 ...
- PHP引用是什么?
引用是什么 在 PHP 中引用意味着用不同的名字访问同一个变量内容.这并不像 C 的指针,替代的是,引用是符号表别名.注意在 PHP 中,变量名和变量内容是不一样的,因此同样的内容可以有不同的名字.最 ...