设为首页 收藏本站
查看: 762|回复: 0

[经验分享] 【Windows核心编程学习笔记】设备I/O的同步与异步

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2015-12-16 08:21:19 | 显示全部楼层 |阅读模式
一、概述
Windows的优势之一是它支持大量的设备类型,这里我们将设备笼统的定义为能够与之通信的任何东西,常见的一些设备及打开方式有:
设备
打开设备的函数
文件
CreateFile()
目录
同上
逻辑磁盘驱动器
同上
物理磁盘驱动器
同上
串口
同上
并口
同上
邮件槽服务器
CreateMailslot()
邮件槽客户端
CreateFile()
命名管道服务器
CreateNamedPipe()
命名管道客户端
CreateFile()
匿名管道
CreatePipe()
套接字
Socket()
控制台
CreateConsoleScreenBuffer() CreateStdHandle()
当然,这里只是简单地列出了各个设备打开的API,具体用法参数可以查阅MSDN或者SDK。当线程与设备发生通信时,即线程发出设备I/O请求时,一般来说有两种方式:
同步设备I/O请求:字如其名,线程发出设备I/O请求后,它会被临时挂起,直到设备完成I/O请求为止,显然这种方式严重损坏性能;
异步设备I/O请求:线程发出设备I/O请求后,不会被临时挂起而是继续执行其他任务,而设备同时继续处理I/O请求,设备处理完成后发送信号给线程,线程接着处理I/O结果。异步设备I/O可以避免线程挂起导致的CPU大量闲置,提高了利用率和吞吐量。
与设备I/O的通信交互是Windows编程中的重点,涉及到诸多的线程的同步方式,尤其是I/O完成端口的引入,使得不管是否与设备I/O关联,都提供了一种有无数种用途的绝佳的线程间通信机制。囿于学力,很多部分的理解都很有限,把自己掌握的整理出来,以便日后再学习吧。这篇笔记的结构大致如下:
Ø 打开和关闭设备
Ø 使用文件设备
Ø 异步设备I/O基础
Ø 接收I/O请求完成通知









二、打开和关闭设备
为了执行任何类型的设备I/O,我们必须先打开想要操作的设备并得到一个句柄,随后我们可以讲该句柄传给许多函数来与设备进行通信。这里同样用这个设备句柄来唯一标识我们的设备。
HANDLE  CreateFile(
PCTSTR  pszName,
DWORD  dwDesiredAccess,
DWORD  dwShareMode,
PSECURITY_ATTRIBUTES  psa,
DWORD  dwCreationDisposition,
DWORD  dwFlagsAndAttributes,
HANDLE  hFileTemplate
);
从之前的表中也可以看出,CreateFile是打开许多设备句柄的关键函数,所以我们对它的参数做一个具体的说明。
l pszName:既可以标识设备的类型,也可以表示该类设备的某个实例。
l dwDesiredAccess:用来指定我们相用什么样的方式来和设备进行数据传输,常见的有四个标志,它们是
0——表示我们不希望对设备读出或写入任何数据,一般用来只想改变设备的配置(比如修改文件的时间戳)
GENERIC_READ——允许对设备进行只读访问
GENERIC_WRITE——允许对设备进行只写访问,比如备份软件或者将数据发送到打印机
GENERIC_READ | GENERIC_WRITE——允许对设备进行读写操作
l dwShareMode:用来指定设备共享特权(device-sharing privilege),但我们打开一个设备但是尚未调用CloseHandle()关闭时,该参数可以控制其他的CreateFile()调用以何种方式打开设备。常见参数有
0——要求独占对设备的访问
FILE_SHARE_READ——只允许共享设备读取方式
FILE_SHARE_WRITE——只允许共享设备写入方式
FILE_SHARE_READ | FILE_SHARE_WRITE——不解释
FILE_SHARE_DELETE——对文件操作时我们不关心文件是否被逻辑删除或者被移动,先将文件打上待删除标记,只有当该文件打开的所有句柄都被关闭的时候再将其真正删除
l Psa指向一个内核对象都具备的安全属性结构,里面可以指定安全信息以及我们是否希望CreateFile返回的句柄能被继承。通常我们传入NULL,这表示用默认的安全设定来创建文件,并且返回的句柄是不可继承的。
l dwCreationDisposition:用来表示用CreateFile打开文件时如果碰到存在的同名文件等情况如何处理,如
CREATE_NEW——若存在同名文件,则调用失败
CREATE_ALWAYS——若存在同名文件,则覆盖
OPEN_EXISTING——若打开的文件或者设备不存咋则调用失败
OPEN_ALWAYS——若打开文件不存在则只直接创建一个
当调用CreateFile打开文件之外的其他设备时,必须将OPEN_EXISTING传给dwCreationDisposition参数

l dwFlagsAndAttributes:允许我们设置一些标志来微调与设备之间的通信;其次我们还可以通过一些属性参数来设置文件属性。比如常见的告诉缓存标志:
FILE_FLAG_NO_BUFFERING告诉高速缓存管理器我们不希望它对任何数据进行缓存,,我们会自己对数据进行缓存;标志FILE_FLAG_DELETE_ON_CLOSE可以让文件系统在文件的所有句柄都被关闭后删除该文件(比如程序运行用到的临时文件,结束后删除,更加隐蔽),标志FLIE_FLAG_BACKUP_SEMANTICS用于备份和恢复软件,不要求文件的全部管理员权限,还有一个重要的标志FILE_FLAG_OVERLAPPED告诉系统我们想要以异步方式来访问设备,默认是同步I/O访问请求。我们重点来介绍一下文件属性参数,可以看到常见的Windows文件属性都有涉及
FILE_ATTRIBUTE_ARCHIVE——存档文件,默认自动设置
FILE_ATTRIBUTE_ENCRYPTED——加密文件
FILE_ATTRIBUTE_HIDDEN——隐藏文件
FILE_ATTRIBUTE_NORMAL——没有其他属性,只有单独使用时才有效
FILE_ATTRIBUTE_SYSTEM——系统文件
FILE_ATTRIBUTE_READONLY——只读文件
OK,可以看到右键文件属性的所有可能这里基本都有涉及,尽管没有向Linux那样提供了丰富的命令行选项,但是WindowsAPI参数中也为我们提供了尽可能多的功能选项。

三、使用文件设备
文件的使用非常普遍,因此重点来讨论下与文件设备有关的问题。首先我们看看如何取得文件的大小。
BOOL  GetFileSizeEx(
   HANDLE  hFile,
   PLARGE_INTEGER  pliFileSize   //联合类型指针
);
hFile当然就是目标文件的句柄了,pliFileSize是一个LARGE_INTEGER联合类型的地址,这个联合体允许我们以一个64位的有符号数的形式来引用一个64位有符号数,或者以两个32位值的形式来引用一个64位有符号数。下面是大概的定义:
Typedef  union  _LARGE_INTEGER {
   Struct  {
      DWORD  LowPart;  //Low 32-bit unsigned value
      LONG   HighPart;   //High 32-bit signed value
};
   LONGLONG  QuadPart;  //Full 64-bit signed value
} LARGE_INTEGER,  *PLARGE_INTEGER;
我们调用GetFileSizeEx可以得到文件的逻辑大小,使用GetCompressedFileSize可以得到文件压缩后的物理大小。
如果要对一个文件读写,其实就是向文件发送一个I/O请求,当然,这里的道理其实不仅适用于文件,对于设备同样适用,如邮件槽、管道、套接字等等。我们通常使用如下函数发送I/O请求,
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
);
执行同步I/OpOverlapped设为NULL,只有在异步I/O时才有意义。
每当调用CreateFile时系统会创建一个文件内核对象来管理对文件的操作,在这个内核对象中维护这一个文件指针,它指向一个64位的偏移量,表示应该在哪里执行下一次同步读取或写入操作,我们称之为文件指针,初始化为0。需要注意的是,调用CreateFile打开同一个文件会得到多个文件内核对象,内部维护的文件指针彼此独立。我们可以使用SetFilePointerEx来设置文件指针的位置
BOOL  SetFilePointerEx(
   HANDLE  hFile,
   LARGE_INTEGER  liDistanceToMove,
   PLAGER_INTEGER  pliNewFilePointer,
   DWORD  dwMoveMethod
);
hFile表示我们想要修改哪个文件内核对象的文件指针。liDistanceToMove告诉系统我们想要移动把指针在dwMoveMethod指定的位置移动多少个字节。使用负数可以将文件指针向后移动。dwMoveMehtod告诉系统如何解释liDistanceToMove,比如常用的参数值为
FILE_BEGIN——从文件头开始计算liDistanceToMove,之和为当前的文件指针
FILE_CURRENT——从当前的文件指针位置开始计算
FILE_END——在文件末尾开始计算
我们给出一个小例子
//本程序用来实验联系Windows关于文件读写操作的API
//命令行启动,参数作为写入文件的内容

#include
#include
#include
#include
using namespace std;

int main(int argc, char *argv[])
{
//操作一个文件之前必须先调用CreateFile()函数以指定的方式打开一个文件
//访问方式:GENERIC_READ |GENERIC_WRITE
//共享模式:当文件被打开时,其他程序可以对该文进进行的操作,表示不允许同时对任何操作
//创建方式:当文件存在或者不存在时的处理策略——CREATE_ALWAYS, CREATE_NEW, OPEN_ALWAYS, OPEN_EXISTING
//新创建的文件属性:ARCHIVE(存档),HIDDEN, SYSTEM, READONLY
//模板文件:没有则设为NULL
HANDLE hFile;
hFile = CreateFile(L"C:\\Documents and Settings\\admin\\桌面\\FileAPITest.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{

cout

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-151736-1-1.html 上篇帖子: Windows异步I/O和完成端口 下篇帖子: 如何进行windows数据恢复呢
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表