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

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

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

HANDLE CreateFile(  LPCTSTR lpFileName,  DWORD dwDesiredAccess,  DWORD dwShareMode,  LPSECURITY_ATTRIBUTES lpSecurityAttributes,  DWORD dwCreationDisposition,  DWORD dwFlagsAndAttributes,  HANDLE hTemplateFile
);  

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

dwDesiredAccess可以是0,代表我们不希望从设备中读取或写入数据,只是想改变它的配置,如时间戳,该值还可以是GENERIC_READ等

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哦。

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是返回文件逻辑大小。

DWORD SetFilePointer(  HANDLE hFile,  LONG lDistanceToMove,         //低32位移动距离,可以为负值  PLONG lpDistanceToMoveHigh,   //高32位移动距离,可以为NULL  DWORD dwMoveMethod            //FILE_BEGIN,FILE_CURRENT,FILE_END
);  BOOL SetFilePointerEx(  HANDLE hFile,  LARGE_INTEGER liDistanceToMove,   //移动距离  PLARGE_INTEGER lpNewFilePointer,  //当前文件指针位置,如果移动距离为0,就可以查询当前指针位置  DWORD dwMoveMethod
);  

SetFilePointer的几点说明:
1:将文件指针设置超过文件大小是可行的,并且可以在该位置写入数据,这样文件大小将增大,还可以通过调用SetEndOfFile()增大或者切断文件
2:如果文件是被FILE_FLAG_NO_BUFFERING打开的,则文件指针只能被设置为扇区大小的整数倍
4.3:SetEndOfFile()可以切断或增大文件大小
同步异步IO基本函数

BOOL ReadFile(  HANDLE hFile,  LPVOID lpBuffer,  DWORD nNumberOfBytesToRead,  LPDWORD lpNumberOfBytesRead,  //异步IO情况下此值没有意义,应传NULL  LPOVERLAPPED lpOverlapped  );
BOOL WriteFile(  HANDLE hFile,  LPCVOID lpBuffer,  DWORD nNumberOfBytesToWrite,  LPDWORD lpNumberOfBytesWritten,    //异步IO情况下此值没有意义,应传NULL  LPOVERLAPPED lpOverlapped  );
BOOL FlushFileBuffers(  HANDLE hFile  );  

异步IO在CreateFile()中要制定FILE_FLAG_OVERLAPPED

typedef struct _OVERLAPPED
{    ULONG_PTR Internal;         //错误码,一旦发出一个异步IO请求,此值被初始化为STATUS_PENDING,如果IO完成,则变为其他值  //我们可以通过宏HasOverlappedIoCompleted(pOverlapped)检查IO是否完成,这个宏本质就是返回  //return Internal!=STATUS_PENDING  ULONG_PTR InternalHigh;     //IO请求完成时,保存已传输的字节数  DWORD Offset;               //下面解释  DWORD OffsetHigh;    HANDLE hEvent;              //以后解释
}OVERLAPPED;  

采用异步IO打开文件设备,文件指针是没有意义的,因为有多个异步操作同时进行,我们无法对文件指针进行同步,所以Offset和OffsetHigh指定从文件那个地方开始执行异步IO操作

如果不是文件设备,必须\将Offset和OffsetHigh设为0

异步IO完成时,会收到一个OVERLAPPED指针,这就是调用异步IO函数传递的那个OVERLAPPED指针,我们可以写一个C++类从OVERLAPPED派生,这样就能添加更多的数据
几个注意事项:

1:异步IO不会按照你的投递顺序来执行,驱动会选择他认为最快的方式来组合这些投递

2:错误处理,以文件IO为例,当我们投递一个异步ReadFile()时,设备驱动程序可能会以同步方式执行,例如如果设备驱动程序发现要读取的数据在文件缓冲里时,就不会投递这个异步设备IO,而是直接将数据复制进我们的缓冲区

如果IO是同步方式执行,ReadFile()和WriteFile()返回非零值,如果是异步或者出现错误,返回FALSE,调用GetLastError()获得错误码,错误码列举一部分

ERROR_IO_PENDING                //异步IO投递成功  ERROR_INVALID_USER_BUFFER
ERROR_NOT_ENOUGH_MEMORY         //每个设备驱动程序会在非分页缓冲池中维护一个固定大小的列表来管理
//待处理的IO请求,如果这个列表已满,异步IO就会失败,返两个错误中的一种,具体返回那个要看设备驱动程序  ERROR_NOT_ENOUGH_QUOTA          //某些设备要求我们提供的数据缓存页面锁定,也就是不能被唤出内存的页面,
//但是系统对一个进程能够锁定的页面数量做了限制,如果超过这个数量,则会返回此错误
//例如如果指定了FILE_FLAG_NO_BUFFERING,就必须满足页面锁定要求
//可以调用SetProcessWorkingSetSize()来增加进程能锁定页面数量的配额  

在异步IO完成之前,不能删除OVERLAPPED结构
取消设备中的异步IO请求

1:调用BOOL CancelIo(HANDLE hd);关闭调用线程添加的所有IO请求(完成端口除外)

2:关闭设备句柄,这将取消其所有IO请求

3:线程终止,会取消此线程所有IO请求(完成端口除外)

4:调用BOOL CancelIoEx(HANDLE hd,LPOVERLAPPED pOVerLapped);如果pOVerLapped为0,则取消此线程投递的所有IO请求,如果不为0,则取消相应的IO请求而不管是不是本线程投递的

1:触发设备内核对象

HANDLE hd=CreateFile(...,FILE_FLAG_OVERLAPPED,...);
...//初始化其他数据
bool  bReadDone=ReadFile(...);//这会将文件句柄设置为未触发状态
DWORD dwError=GetLastError();  if(bReadDone==false&&(dwError==ERROR_IO_PENDING))
{  WaitForSingleObject(hd,INFINITE);  ...//查看OVERLAPPED结构中错误码和实际传输字节数
}
else
{  //此异步IO被同步执行,或者发生了其他错误,检查dwError具体值
}  

这个方法不能投递多个异步IO,因为设备内核对象触发时,我们不能确定投递的那一个IO请求完成了
如果不用这种方式,为了略微提高性能,我们应该关闭操作系统触发设备内核对象,调用如下函数
BOOL SetFileCompletionNotificationModes(HADNLE hd,UCHAR uFlags);为uFlags传入FILE_SKIP_SET_EVENT_ON_HANDLE
2:触发事件内核对象
OVERLAPPED有一个hEvent,它用来标识一个事件内核对象,我们每投递一个异步IO,就为其添加一个事件内核对象,这样,当一个异步IO完成时,设备驱动会去检查这个hEvent的值,如果有值,则会SetEvent(), 我们就可以调用WaitForMultipleObjects()来等待这些事件内核对象,并通过返回值确定是那个异步IO完成了

可提醒IO

1:每个线程都有一个与其相关的队列,叫做异步过程调用(APC)

2:使用APC执行异步函数如下

CreateFileEx(...,LPOVERLAPPED_COMPLETION_ROUTINE pfn);

pfn别成为完成函数

(WINAPI *LPOVERLAPPED_COMPLETION_ROUTINE)(  __in    DWORD dwErrorCode,  __in    DWORD dwNumberOfBytesTransfered,  __inout LPOVERLAPPED lpOverlapped  );  

3.系统将已经完成的异步IO投递到APC队列中的顺序是随机的

4:调用

SleepEx();
WaitForSingleObjectEx();
WaitForMultipleObjectsEx();
SignalObjectAndWait();
GetQueuedCompletionStatusEx();
MsgWaitForMultipleObjectsEx();  

中的一个将线程置为可提醒状态(很多最后一个值需设置为TRUE)

只要APC队列中有一项,线程就不会被挂起,转而去执行完成函数,如果APC队列为空,线程被挂起,直到APC队列不为空或者等待的内核对象被触发或者超时或者一个互斥量被遗弃

这6个函数如果是因为APC返回,返回值为WAIT_IO_COMPLETION,GetLastError()也是此值

可提醒IO不会试图去触发OVERLAPPED的事件对象,所以我们可以据为己有

可提醒IO的缺点:

发出IO请求的线程必须对其进行处理,即使其他线程处于空闲状态,效率低下

采用APC通知一个线程优雅退出的方式

//手动添加APC
DWORD QueueUserAPC(     //返回值实际是BOOL值,表示添加成功与否
__in PAPCFUNC pfnAPC,   //回调函数,可以跨进程,那么此函数也应该在相应的地址空间
__in HANDLE hThread,
__in ULONG_PTR dwData   //传个回调函数的一个参数而已
);
//回调函数原型
VOID (APIENTRY *PAPCFUNC)(  __in ULONG_PTR dwParam);  //采用APC通知一个线程优雅退出的方式
void ThreadFun()
{  ...;  DWORD dw=WaitForSingleObjectEx(hd,INFINITE,TRUE);  if(dw==WAIT_OBJECT_0)  {  ...;  }  else if(dw==WAIT_IO_COMPLETION)  {  QUIT;  }
}  

同步设备IO与异步设备IO相关推荐

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

    <Windows核心编程系列>九谈谈同步设备IO与异步设备IO之同步设备IO 同步设备IO 所谓同步IO是指线程在发起IO请求后会被挂起,IO完成后继续执行. 异步IO是指:线程发起IO请 ...

  2. 两段文章清楚弄明白什么是异步IO、同步IO、同步阻塞IO、同步非阻塞IO、异步阻塞IO、异步非阻塞IO

    百科解释:异步IO_百度百科 先看2,再看1,会理解的更好! 1. 2.阻塞和非阻塞 # 阻塞和非阻塞关注的是程序在等待调用结果时的状态 # 阻塞调用是指调用结果返回之前,当前线程会被挂起.调用线程只 ...

  3. 同步阻塞,同步非阻塞,异步阻塞,异步非阻塞IO

    在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Proactor运用于异步I/O操作. 在比较这两个模式之前,我们首先的搞明白 ...

  4. node.js异步式IO与事件式编程

    Node.js最大的特性就是异步式I/O与事件紧密结合的编程模式.这种模式与传统的同步式IO线性的编程思路有很大的不同,因为控制流很大程度上要靠事件和回调函数来组织,一个逻辑要拆分为若干个单元格. 内 ...

  5. python io多路复用_Python之IO多路复用

    一.IO模型介绍 ​ 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?这 ...

  6. 设备IO之一(mmap、直接IO以及异步IO)

    现在,在linux中经常可以看到在用户空间编写的驱动程序,比如X服务器,一些厂商的私有驱动等等,这就意味着用户空间取得了对硬件的访问能力,这通常是通过mmap将设备内存映射到了用户进程空间,从而使得用 ...

  7. 【多线程】0.理解一下5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO

    5种IO模型.阻塞IO和非阻塞IO.同步IO和异步IO 看了一些文章,发现有很多不同的理解,可能是因为大家入切的角度.环境不一样.所以,我们先说明基本的IO操作及环境. 本文是在<UNIX网络编 ...

  8. Windows内核原理-同步IO与异步IO

    目录 Windows内核原理-同步IO与异步IO 背景 目的 I/O 同步I/O 异步I/O I/O完成通知 总结 参考文档 Windows内核原理-同步IO与异步IO 背景 在前段时间检查异常连接导 ...

  9. 5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO

    5种IO模型.阻塞IO和非阻塞IO.同步IO和异步IO 看了一些文章,发现有很多不同的理解,可能是因为大家入切的角度.环境不一样.所以,我们先说明基本的IO操作及环境.本文是在<UNIX网络编程 ...

最新文章

  1. 关于Android这个名字。。。
  2. 大厂常见笔试题 滑动窗口内数的和
  3. 英语语法---名词详解
  4. c++申请内存空间_有没有想过:malloc分配的内存空间地址连续吗
  5. dapper 连接mysql_如何在.NET中使用Dapper处理数据库连接?
  6. spark大数据基础概念
  7. 根据地址形式分辨scala的一维数组和二维数组
  8. Activity、Task、应用和进程
  9. 反射机制——获取Class中的构造函数
  10. php能干哪些副业,做副业,在能干的基础上踏实肯干
  11. Angular 7和.NET Core 2.2——全球天气(第3部分)
  12. bzoj 1005: [HNOI2008]明明的烦恼(prufer数列)
  13. Linux 服务器之间文件传输
  14. Web Server与App Server
  15. oracle索引有哪些分类,Oracle中的索引分类
  16. Lua代码加密 LuaJit代码加密
  17. 1384. 按年度列出销售总额
  18. python教程马哥_【60集全】全新马哥教育 运维必备python基础语法全讲解_IT教程网...
  19. 同一个PDF如何同时在两个窗口并排显示?
  20. 中国医科大学网络教育学院计算机应用基础,中国医科大学网络教育学院试卷.doc...

热门文章

  1. 计算机应用基础1,计算机应用基础1
  2. mysql 镜像数据_mysql官方镜像数据存储问题
  3. lisp 线性标注自动避让_《数据标注工程》第一章学习笔记及作业:数据标注概述...
  4. ps如何修改图片大小尺寸_PS常用操作 | 图片的裁剪、拼接、尺寸调整... ...
  5. Adobe illustrator 多个对象进行环形布局 - 连载22
  6. 加拿大生信开源学习资源Bioinformatics.ca
  7. jquery select css样式,css配合jquery美化 select
  8. python语言的单行注释以井号开头_python-注释
  9. excel三次样条函数_Excel中F1到F12键的神奇用法
  10. Vue第一部分(5):计算属性和过滤器