本章回答了如下几个问题:

◆ 什么是Overlapped I/O?为什么需要Overlapped I/O?如何让数据传输支持Overlapped I/O?

◆ 数据传输结束后,Win32提供了哪些方式对用户进行通告,以便进行适当的善后?

◆ 影响线程优先级的因素有哪些?如何获取或设置进程线程优先级?优先级的改变容易带来哪些问题?又该如何应对?

◆ 什么是被激发的文件句柄?什么是被激发的事件?什么是异步进程调用(APCs)?这些方式各是如何实现Overlapped I/O的?各有何优缺点?

◆ 使用Overlapped I/O的初衷是使“受制于I/O的程序”中获得高效率。但是否是各种情况下Overlapped I/O都能提高系统效率吗?

◆ 什么是I/O completion port?这个机制是怎么工作的?有什么优点?为什么说此方式能够很好地支持scalable(可升级的)系统?而对于工作于此模式下的文件,从提高系统效率考虑,怎样才能避免无谓的completion packet通告呢?

什么是Overlapped I/O?

Overlapped I/O,常被设计为多线程处理,以便在一个“受制于I/O的程序”中获得高效率。

利用Win32所谓的overlapped I/O特性,可以让I/O操作并行处理,并且当一个I/O完成时,程序会收到一个通告。有些系统把这个特性称为非阻塞I/O,或异步I/O。

overlapped I/O是Win32的一项技术,你可以要求操作系统为你传送数据,并且传送完毕后通知你。这项技术使你的程序在I/O进行中仍然能够继续处理事务。事实上,操作系统内部正是以线程来完成overlapped I/O。这样,你可以获得线程的所有利益,而不需付出痛苦代价。

在Windows 95环境下,Overlapped I/O使用有些限制。它不支持磁盘或光盘中的文件操作。

Win32文件操作函数

HANDLE CreateFile(

LPCTSTR lpFileName,          // pointer to name of the file

DWORD dwDesiredAccess,       // access (read-write) mode

DWORD dwShareMode,           // share mode

LPSECURITY_ATTRIBUTES lpSecurityAttributes,// pointer to security attributes

DWORD dwCreationDisposition,  // how to create

DWORD dwFlagsAndAttributes,  // file attributes

HANDLE hTemplateFile         // handle to file with attributes to copy

);

BOOL ReadFile(

HANDLE hFile,                // handle of file to read

LPVOID lpBuffer,             // pointer to buffer that receives data

DWORD nNumberOfBytesToRead,  // number of bytes to read

LPDWORD lpNumberOfBytesRead, // pointer to number of bytes read

LPOVERLAPPED lpOverlapped    // pointer to structure for data

);

BOOL WriteFile(

HANDLE hFile,                    // handle to file to write to

LPCVOID lpBuffer,                // pointer to data to write to file

DWORD nNumberOfBytesToWrite,     // number of bytes to write

LPDWORD lpNumberOfBytesWritten,  // pointer to number of bytes written

LPOVERLAPPED lpOverlapped        // pointer to structure for overlapped I/O

);

BOOL CloseHandle(

HANDLE hObject   // handle to object to close

);

一些说明:

CreateFile()可处理的对象包括:files、pipes、mailslots、communication resource、disk device(Windows NT only)、consoles、directories(open only)。

如果要使用异步I/O,CreateFile()时设置dwFlagsAndAttributes含FILE_FLAG_OVERLAPPED标志,通知操作系统对此文件的访问采取异步I/O模式。

由于异步I/O模式下,可以在同一时间读(或写)文件的许多部分,所以没有“目前的文件位置”这一概念。每次读写都必须包含其文件位置。

OVERLAPPED结构

typedef struct _OVERLAPPED {

DWORD  Internal;

DWORD  InternalHigh;

DWORD  Offset;

DWORD  OffsetHigh;

HANDLE hEvent;

} OVERLAPPED;

说明:

Internal

通常它被保留。然而,当GetOverlappedResult()返回FALSE,GetLastError()返回ERROR_IO_PENDING时,这个栏位将内含一个视系统而定的状态。

InternalHigh

通常它被保留。然而,当GetOverlappedResult()返回TRUE时,这个栏位将内含“被传输的数据长度”。

Offset

定义文件开始读(或写)的开始位置,以字节为单位。该偏离位置从文件头开始起算。如果目标设备不支持文件位置(比如管道),此栏忽略。

OffsetHigh

64位的文件偏离量,较高的32位。如果目标设备不支持文件位置(比如管道),此栏忽略。

hEvent

一个手动事件。当overlapped I/O完成后即被激发。ReadFileEx()和WriteFileEx()忽略这个栏位,彼时它可能用来传输一个用户自定义的指针。

注意: OVERLAPPED结构的生命周期应超越ReadFile()和WriteFile()函数。

被激发的File Handles

注意,此方式在Windows95/98下不被支持。详看MSDN中关于GetOverlappedResult()函数的说明。

在Windows NT下,最简单的overlapped I/O类型,是使用它自己的文件句柄作为同步机制。大致流程如下:

  1. 调用CreateFile(),设置dwFlagsAndAttributes含FILE_FLAG_OVERLAPPED标志,通知操作系统对此文件的访问采取异步I/O模式;
  2. 建立一个OVERLAPPED结构,其中包含I/O请求所需参数;
  3. 调用ReadFile()或WriteFile(),最后一个参数为OVERLAPPED结构为参数。此时Win32会在后台处理你的请求;
  4. 使用wait…()函数,并将该文件的文件句柄作为参数,等待操作结束。文件句柄是核心对象,一旦操作完成即被激发;
  5. 使用GetOverlappedResult()函数,查询操作结果。此时你获得的结果和调用ReadFile()、WriteFile()而没指定overlapped I/O所传回的东西一样。
  6. 文件使用完毕,CloseHandle()。

BOOL GetOverlappedResult(

HANDLE hFile,                       // handle to file, pipe, or comm device

LPOVERLAPPED lpOverlapped,          // pointer to overlapped structure

LPDWORD lpNumberOfBytesTransferred, // pointer to actual bytes count

BOOL bWait                          // wait flag

);

说明:如果bWait设置为FALSE,如果overlapped I/O还没完成,函数返回FALSE,调用GetLastError()会返回ERROR_IO_INCOMPLETE。所以,调用GetOverlappedResult()时如果bWait设置为FALSE,可即时查询overlapped I/O的状态。

举例:

hFile = CreateFile( szPath,

GENERIC_READ,

FILE_SHARE_READ|FILE_SHARE_WRITE,

NULL,

OPEN_EXISTING,

FILE_FLAG_OVERLAPPED,

NULL);

if (hFile == INVALID_HANDLE_VALUE)

{

return -1;

}

// Initialize the OVERLAPPED structure

memset(&overlap, 0, sizeof(overlap));

overlap.Offset = 1500;

// Request the data

rc = ReadFile( hFile,buf,READ_SIZE,&numread,&overlap);

if (rc)

{

// The data was read successfully

}

else

{

if (GetLastError() == ERROR_IO_PENDING)

{

VERIFY(WAIT_OBJECT_0 == WaitForSingleObject(hFile, INFINITE));

rc = GetOverlappedResult(hFile, &overlap,&numread,FALSE);

}

else

{

// Something went wrong

}

}

CloseHandle(hFile);

说明:

  • 来源:(http://blog.sina.com.cn/s/blog_5678943c0100d6fc.html) - 第六章 Overlapped I/O,在你身后变戏法(1)_流水_新浪博客

    虽然你要求一个overlapped操作,但如果数据已被放入cache中,或操作系统认为它可以快速地取得那份你所要求的数据,那么文件操作会在ReadFile()返回前完成。此时ReadFile()返回TRUE。
  • 如果你要求一个文件操作为overlapped操作,而操作系统把这个“操作请求”放到队列中等待执行,那么ReadFile()或WriteFile()都会传回FALSE以示失败。但函数返回FALSE有多种原因,可调用GetLastError()调查原因,如果是ERROR_IO_PENDING,那说明“操作请求”被放入队列等待执行;其它值,那可能真正代表一个错误了。
  • OVERLAPPED结构中包含了“这个操作从哪里开始的信息”,可以处理64位的偏移量。
  • WaitForSingleObject(hFile, INFINITE); rc = GetOverlappedResult(hFile, &overlap,&numread,FALSE);其实可合并为一句:rc = GetOverlappedResult(hFile, &overlap,&numread,TRUE);

被激发的事件对象

使用文件句柄作为激发机制,有一个明显的限制:如果多个线程对同一个文件进行操作,由于只有一个相同的handle,对于每个可能进行的overlapped操作都调用GetOverlappedResult()查看操作是否完成,这将不是一个很有效率的做法——因为很多的时候并不是自己所期待的操作完成了。另外,Windows95/98下不可以使用文件句柄作为激发机制。

OVERLAPPED结构中的最后一个栏位是一个事件句柄。如果使用文件句柄作为激发对象,可将该位设置为NULL。如果该位被设定为一个事件对象时,系统核心会在overlapped操作完成后,自动设置此事件为激发。

由于每个overlapped操作都有它独一无二的OVERLAPPED结构,每个结构都有它独一无二的事件对象,用以代表该操作。

注意:OVERLAPPED结构中的事件必须是手动事件。否则,如果事件为自动事件,由于系统核心可能会在你有机会等待该事件前就激发它,而自动事件的激发状态是不能保留的,于是事件遗失,这将导致你的等待永远无法返回。

使用手动事件配搭overlapped I/O,就可以对同一个文件发出多个读取操作和多个写入操作,每个操作都有自己的事件对象。然后调用wait…()函数等待其中之一或全部完成。

异步过程调用(Asynchronous Procedure Calls,APCs)

使用overlapped I/O配搭手动事件,会产生两个问题:

  1. 使用WaitForMultipleObjects()最多只能等待MAXIMUM_WAIT_OBJECTS个对象。对于Win32,此值目前为64。如果等待的对象超过64,就会出问题。
  2. 必须不断地根据“哪一个事件被激发”而计算如何反应。

异步过程调用可解决此问题。此时只要使用“Ex”版的ReadFile()和WriteFile()。这两函数可额外指定一个参数,定义一个callback函数。当一个overlapped I/O完成时,系统调用该callback函数。这个callback函数被称为I/O completion routine,因为,系统是在一个特定的overlapped I/O完成后调用它的。

但是,需要注意的是,一个特定的overlapped I/O完成后,Windows并不会贸然中断你的程序,然后调用你所提供的callback函数。那显然可能会带来新的问题。只有线程说“好,现在是一个安全时机”时系统才会调用你的callback函数。以Windows的说法,只有线程处于所谓的“alertabe”状态,回调函数才会被执行,否则对I/O completion routine的调用会被暂时搁置下来。因此,当一个线程终于处于“alertabe”状态时,可能有一堆储备的APCs等待被处理。

如果线程因为以下5个函数而处于等待状态,而其“alertabe”标志被设置为TRUE,则该现程就是处于“alertabe”状态:

DWORD SleepEx(

DWORD dwMilliseconds,  // time-out interval in milliseconds

BOOL bAlertable        // early completion flag

);

DWORD WaitForSingleObjectEx(

HANDLE hHandle,        // handle to object to wait for

DWORD dwMilliseconds,  // time-out interval, in milliseconds

BOOL bAlertable        // return to execute I/O completion routine if TRUE

);

DWORD WaitForMultipleObjectsEx(

DWORD nCount,             // number of handles in handle array

CONST HANDLE *lpHandles,  // points to the object-handle array

BOOL fWaitAll,            // wait flag

DWORD dwMilliseconds,     // time-out interval in milliseconds

BOOL bAlertable           // alertable wait flag

)

DWORD MsgWaitForMultipleObjectsEx(

DWORD nCount,          // number of handles in handle array

LPHANDLE pHandles,     // pointer to an object-handle array

DWORD dwMilliseconds,  // time-out interval in milliseconds

DWORD dwWakeMask,      // type of input events to wait for

DWORD dwFlags          // wait flags

);

DWORD SignalObjectAndWait(

HANDLE hObjectToSignal,  // handle to object to signal

HANDLE hObjectToWaitOn,  // handle to object to wait for

DWORD dwMilliseconds,    // time-out interval in milliseconds

BOOL bAlertable          // alertable flag

);

只有使用上面这些函数进行等待ReadFileEx()或WriteFileEx()操作,回调函数才会被执行。

回调函数解释

VOID CALLBACK FileIOCompletionRoutine(

DWORD dwErrorCode,                // completion code

DWORD dwNumberOfBytesTransfered,  // number of bytes transferred

LPOVERLAPPED lpOverlapped         // pointer to structure with I/O information

);

dwErrorCode:0表示操作完成,ERROR_HANDLE_EOF表示操作到文件尾

dwNumberOfBytesTransfered:真正被传输的数据字节数

lpOverlapped:指向OVERLAPPED结构,由开启overlapped I/O操作的函数提供。由于APCs时OVERLAPPED结构中hEvent栏位不需要用来放置一个事件句柄,此栏程序员可自由运用,比如用作回调函数的入口参数。

对文件进行Overlapped I/O的缺点

在Windows NT测试时发现,Windows NT似乎是以“I/O请求”的大小来决定是否进行overlapped I/O。如果数据量较小,系统实际上总是采取非overlapped方式进行文件读取;数据量略大些,采取Overlapped I/O其实比单纯调用ReadFile()并无优越性,相反地,效率降低。如果文件数据量比较大,Overlapped I/O才能凸显其优越性。

[转]重叠IO的更多相关文章

  1. Socket重叠IO

    1.为什么到现在才弄懂这个 不知道这个Socket重叠IO这种模型是不是socket IO完成端口的基础,不过我感觉,学习一下这个再去学习socket IO完成端口是比较有好处的. 这个Scoket重 ...

  2. winSocket编程(九)重叠IO

    重叠模型的优点 重叠模型的基本原理 关于重叠模型的基础知识 重叠模型的实现步骤 多客户端情况的注意事项 一.重叠模型的优点 1.可以运行在支持Winsock2的所有Windows平台 ,而不像完成端口 ...

  3. 重叠IO

    一. 异步IO        说到重叠模型首先还是提一下异步IO比较好,因为从本质上讲,重叠模型也是一种异步IO模型.       我们知道,相对于计算机执行的其他操作而言,设备IO(文件.管道.套接 ...

  4. 重叠io操作

    第一章 一. 重叠模型的优点 1. 可以运行在支持Winsock2的所有Windows平台 ,而不像完成端口只是支持NT系统. 2. 比起阻塞.select.WSAAsyncSelect以及WSAEv ...

  5. WinSock 重叠IO模型

    title: WinSock 重叠IO模型 tags: [WinSock 模型, 网络编程, 重叠IO模型] date: 2018-06-29 20:26:13 categories: Windows ...

  6. windows网络模型之重叠IO的使用

    大部分内容转载自https://blog.csdn.net/piggyxp/article/details/114883 目录: 1. 重叠模型的优点 2. 重叠模型的基本原理 3. 关于重叠模型的基 ...

  7. 四.Windows I/O模型之重叠IO(overlapped)模型

    1.适用于除Windows CE之外的各种Windows平台.在使用这个模型之前应该确保该系统安装了Winsock2.重叠模型的基本设计原理是使用一个重叠的数据结构,一次投递一个或多个Winsock ...

  8. 重叠IO 模型

    1. 重叠模型的优点 2. 重叠模型的基本原理 3. 关于重叠模型的基础知识 4. 重叠模型的实现步骤 5. 多客户端情况的注意事项 一.重叠模型的优点   1.可以运行在支持Winsock2的所有W ...

  9. 重叠IO overlapped I/O 运用详解

    2009年02月21日 星期六 下午 07:54 I/O设备处理必然让主程序停下来干等I/O的完成,对这个问题有 方法一:使用另一个线程进行I/O.这个方案可行,但是麻烦.               ...

  10. windows网络模型之重叠IO(完成例程)的使用

    #include <WINSOCK2.H> #include <stdio.h> #define PORT 5150 #define MSGSIZE 1024 #pragma ...

随机推荐

  1. 安卓Activity界面切换添加动画特效

    在Android 2.0之后有了overridePendingTransition() ,其中里面两个参数,一个是前一个activity的退出两一个activity的进入, @Override pub ...

  2. mysql 5.7.12 新增 X plugin 详解

     https://dev.mysql.com/doc/refman/5.7/en/document-store.html   原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息 ...

  3. php中序列化与反序列化在utf8和gbk编码中测试

    在php中如果我们统一编码是没有什么问题了,但是很多朋友会发现一个问题就是utf8和gbk编码中返回的值会有所区别: php 在utf8和gbk编码下使用serialize和unserialize互相 ...

  4. 电商平台如何接入快递鸟电子面单API?

    快递鸟是全球物流接口服务商,为电商 ERP.电商平台.仓储.清关公司提供物流跟踪.电子面单.智选物流.物流金融.在线下单等服务,解决电商的物流管理模块和金融模块.现就对快递鸟电子面单API做基本描述, ...

  5. Android 高级UI设计笔记02:可以拖动交换item位置的GridView(转载)

    如果大家不知道GridView基本使用,可以先参见:Android(java)学习笔记154:使用GridView以及重写BaseAdapter 1. 首先我们明白GridView拖拽的思路: ()根 ...

  6. Debian 7 安装 Docker

    Debian 7更新内核到3.16后 一.添加docker源 在source.list中加入: # Docker Repo deb https://get.docker.io/ubuntu docke ...

  7. memcached linux / win32 1.4.13

    memcached-win32-1.4.13 点击下载 http://pan.baidu.com/s/1kTMABaf memcached -d install (安装为windows service ...

  8. xmpp搭建服务器

    二.环境配置1.安装mysql2.修改mysql的帐户的密码>sqlite(移动平台) ,是没有密码直接连接数据库>mysql sqlServer (服务端的数据库) 是有帐户和密码  默 ...

  9. 【AR】增强现实安卓编程 - Vuforia SDK 的安装和使用 (Android Studio)

    Vuforia是个强大的AR平台.使用Vuforia API 可以实现物体识别,图片追踪,柱型追踪,多对象追踪,自定义目标追踪,云识别,文字识别,帧标识和虚拟按钮等功能. 它支持Android, iO ...

  10. [改善Java代码]多线程使用Vector或HashTable

    Vector是ArrayList的多线程版本,HashTable是HashMap的多线程版本,这些概念我 们都很清楚,也被前辈嘱咐过很多次,但我们经常会逃避使用Vector和HashTable,因为用 ...