1、演示内容

文件复制

2、提要

复制大文件时,使用FILE_FLAG_NO_BUFFERING标志

同时需要注意:

读写文件的偏移地址为 磁盘扇区 的整数倍

读写文件的字节数为 磁盘扇区 的整数倍

读文件到的缓冲区在进程地址空间中的地址为 磁盘扇区 的整数倍

3、JUST CODING

#include "stdafx.h"
#include <Windows.h>
#include <process.h>
#include <iostream>
using namespace std; //完成键
#define CK_READ 1
#define CK_WRITE 2 void ShowErrMsg(LPCSTR lpMsg); //传给线程函数的参数
typedef struct _tagThreadParam
{
HANDLE hIOCP; //IOCP
LPVOID lpAddr; //读入的内存地址
LARGE_INTEGER liFileSize; //源文件大小
size_t nDataBlockSize; //每次读写的数据块大小
HANDLE hSrc; //源文件
HANDLE hDest; //目的文件
LPOVERLAPPED lpOLPSrc; //源文件的OVERLAPPED结构指针
LPOVERLAPPED lpOLPDest; //目的文件的OVERLAPPED结构指针
}ThreadParam, *LPTHREADPARAM; int _tmain(int argc, _TCHAR* argv[])
{
/*LPCTSTR lpSrc = TEXT("D:\\SourceSoftware\\myeclipse-8.5.0.rar");
LPCTSTR lpDest = TEXT("D:\\SourceSoftware\\myeclipse-8.5.0_copy.rar");*/ /*LPCTSTR lpSrc = TEXT("D:\\SourceSoftware\\VS2010\\cn_visual_studio_2010_ultimate_x86_dvd_532347.iso");
LPCTSTR lpDest = TEXT("D:\\SourceSoftware\\VS2010\\cn_visual_studio_2010_ultimate_x86_dvd_532347_copy.iso");*/ /*LPCTSTR lpSrc = TEXT("D:\\SourceSoftware\\SQLServer2008\\cn_sql_server_2008_r2_developer_x86_x64_ia64_dvd_522724.iso");
LPCTSTR lpDest = TEXT("D:\\SourceSoftware\\SQLServer2008\\cn_sql_server_2008_r2_developer_x86_x64_ia64_dvd_522724_copy.iso");
*/
/*LPCTSTR lpSrc = TEXT("D:\\SourceSoftware\\SQLServer2008\\cn_sql_server_2008_r2_developer_x86_x64_ia64_dvd_522724.rar");
LPCTSTR lpDest = TEXT("D:\\SourceSoftware\\SQLServer2008\\cn_sql_server_2008_r2_developer_x86_x64_ia64_dvd_522724_copy.rar");*/ LPCTSTR lpSrc = TEXT("D:\\SourceSoftware\\VS2012旗舰版\\VS2012_ULT_chs.iso");
LPCTSTR lpDest = TEXT("D:\\SourceSoftware\\VS2012旗舰版\\VS2012_ULT_chs_copy.iso"); HANDLE hSrcFile = INVALID_HANDLE_VALUE; //源文件句柄
HANDLE hDestFile = INVALID_HANDLE_VALUE; //目标文件句柄
HANDLE hIOCP = NULL; //IOCP
LPVOID lpAddr = NULL; //申请内存地址 __try
{
cout << endl << "开始打开源文件" <<endl;
//源文件
hSrcFile = CreateFile(
lpSrc, //源文件
GENERIC_READ, //读模式
FILE_SHARE_READ, //读共享
NULL, //安全属性
OPEN_EXISTING, //必须存在
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,//异步 | 不用缓存
NULL //文件模板为空
);
if(INVALID_HANDLE_VALUE == hSrcFile)
{
ShowErrMsg("源文件打开错误");
return -;
} cout << endl << "开始打开目的文件" << endl; //目的文件
hDestFile = CreateFile(
lpDest, //目的文件
GENERIC_WRITE, //写模式
, //独占访问
NULL, //安全属性
CREATE_ALWAYS, //总是创建
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, //异步 | 不用缓存
hSrcFile //文件属性同源文件
);
if (INVALID_HANDLE_VALUE == hDestFile)
{
ShowErrMsg("目的文件打开错误");
return -;
} cout << endl << "开始获取文件尺寸" << endl;
//源文件尺寸
LARGE_INTEGER liFileSize;
BOOL bRet = GetFileSizeEx(hSrcFile, &liFileSize);
if (FALSE == bRet)
{
ShowErrMsg("获取源文件尺寸失败");
return -;
} cout << endl << "开始用源文件尺寸设置目的文件大小" << endl; //设置目的文件指针位置为源文件尺寸 并 设置文件尾
BOOL bRet2 = SetFilePointerEx(hDestFile, liFileSize, NULL, FILE_BEGIN);
BOOL bRet3 = SetEndOfFile(hDestFile);
if (FALSE == bRet2 || FALSE == bRet3)
{
ShowErrMsg("设置目的文件尺寸失败");
return -;
} cout << endl << "开始获取磁盘扇区大小 和 系统信息" << endl;
SYSTEM_INFO sysInfo = {};
GetSystemInfo(&sysInfo);
DWORD dwBytesPerSector = 0UL;
bRet = GetDiskFreeSpace(TEXT("D:"), NULL, &dwBytesPerSector, NULL, NULL);
if (FALSE == bRet)
{
ShowErrMsg("开始获取磁盘扇区大小 错误");
return -;
} //读
OVERLAPPED ovlpRead;
ovlpRead.Offset = ;
ovlpRead.OffsetHigh = ;
ovlpRead.hEvent = NULL; //写
OVERLAPPED ovlpWrite;
ovlpWrite.Offset = ;
ovlpWrite.OffsetHigh = ;
ovlpWrite.hEvent = NULL; //创建IOCP 并和 文件关联
hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, , sysInfo.dwNumberOfProcessors);
if (NULL == hIOCP)
{
DWORD dwErr = GetLastError();
if (ERROR_ALREADY_EXISTS != dwErr)
{
ShowErrMsg("创建IOCP 失败");
return -;
}
} hIOCP = CreateIoCompletionPort(hSrcFile, hIOCP, CK_READ, sysInfo.dwNumberOfProcessors);
hIOCP = CreateIoCompletionPort(hDestFile, hIOCP, CK_WRITE, sysInfo.dwNumberOfProcessors); //申请扇区大小的5倍的内存
size_t sizeMAX = dwBytesPerSector * * * ; //512K * 64 * 2
size_t sizeMIN = dwBytesPerSector * * * ; //申请内存
lpAddr = VirtualAlloc(NULL, sizeMAX, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (NULL == lpAddr)
{
ShowErrMsg("申请内存错误");
return -;
} //先往IOCP的完成队列插入一个 写完成 项
//写0字节
PostQueuedCompletionStatus(
hIOCP, //IOCP
, //GetQueuedCompletionStatus取到的传送字节为0
CK_WRITE, //写操作
&ovlpWrite //写OVERLAPPED
); DWORD dwBytesTrans = ; //传输字节数
ULONG_PTR ulCompleteKey = ; //完成键
LPOVERLAPPED lpOverlapped = NULL; //OVERLAPPED结构
BOOL bLastTime = FALSE; //最后一个读操作
int i = ;
int j = ;
int nCountZero = ; //计数 /************************************************************************/
/* 因为前一次只是往IOCP的完成队列插入了一项【写完成】,而并非真的写
只是让下面的代码从 【读操作】开始,
执行序列为: 读-写, 读-写, ... ,读-写
当每个【读操作】完成时:把缓冲区中的数据写入【目的文件】,并更新【源文件】的偏移量 当每个【写操作】完成时:更新【目的文件】的偏移量,
同时,因为操作序列是写操作在后,因此写操作完成后,根据更新后的【源文件】的偏移量
和【源文件】大小做比较,如果大于等于源文件大小,则说明这是最后一次读取操作,则当下一次
写操作完成时 退出循环。 如果当前【源文件偏移量】没有达到【源文件大小】则再次从【源文件】
中读取数据进缓冲区,
/************************************************************************/
while(TRUE)
{
BOOL bRet = GetQueuedCompletionStatus(hIOCP, &dwBytesTrans, &ulCompleteKey, &lpOverlapped, INFINITE);
if (FALSE == bRet)
{
DWORD dwErr = GetLastError();
if (NULL != lpOverlapped)
{
ShowErrMsg("线程函数返回错误, 错误原因:");
cout << dwErr <<endl;
break;
} //if
else
{
if (ERROR_TIMEOUT == dwErr)
{
ShowErrMsg("等待超时");
}
else
{
ShowErrMsg("线程函数返回错误, 错误原因2:");
cout << dwErr <<endl;
} continue;
} //else } //if //读操作完成
if (ulCompleteKey == CK_READ)
{
cout << endl << "-------------第 " << ++ i << " 次操作完成,开始写文件 ---------------- "<<endl;
WriteFile(hDestFile, lpAddr, sizeMIN, NULL, &ovlpWrite); //读操作完成 更新 源文件的偏移量
LARGE_INTEGER liSrcFile;
liSrcFile.QuadPart = dwBytesTrans; ovlpRead.Offset += liSrcFile.LowPart;
ovlpRead.OffsetHigh += liSrcFile.HighPart;
} //if //写操作完成
else if (ulCompleteKey == CK_WRITE)
{
//写操作完成, 更新目的文件的偏移量
LARGE_INTEGER liDestFile;
liDestFile.QuadPart = dwBytesTrans;
ovlpWrite.Offset += liDestFile.LowPart;
ovlpWrite.OffsetHigh += liDestFile.HighPart; //当前源文件的偏移量
LARGE_INTEGER liTemp;
liTemp.LowPart = ovlpRead.Offset;
liTemp.HighPart = ovlpRead.OffsetHigh;
//当前文件偏移是超过文件大小
if (liTemp.QuadPart >= liFileSize.QuadPart)
{
break;
}
cout << endl << "*************第 " << ++ j << " 次读写操作完成,开始读文件 ***************"<<endl;
ReadFile(hSrcFile, lpAddr, sizeMIN, NULL, &ovlpRead); } //else if } //while SetFilePointerEx(hDestFile, liFileSize, NULL, FILE_BEGIN);
SetEndOfFile(hDestFile); cout << endl << " $$$$$$$$$$$$$$$$$$$$$ 操作完成 $$$$$$$$$$$$$$$$$" <<endl; }
__finally
{
cout << endl << "清理资源" <<endl;
if (INVALID_HANDLE_VALUE != hSrcFile)
CloseHandle(hSrcFile);
hSrcFile = INVALID_HANDLE_VALUE; if(INVALID_HANDLE_VALUE != hDestFile)
CloseHandle(hDestFile);
hDestFile = INVALID_HANDLE_VALUE; if(NULL != lpAddr)
VirtualFree(lpAddr, , MEM_RELEASE | MEM_DECOMMIT);
lpAddr = NULL;
} cout << endl << endl;
return ;
} void ShowErrMsg(LPCSTR lpMsg){ cout << endl << "Some error happened : " << lpMsg << "\n"; }

4、细节和问题

过程中发现:某个文件的复制进入死循环,判断break退出while的条件永远不成立,即【目的文件的偏移量】没有达到【源文件的大小】这一条件,单步过程中发现是如下问题

另外:关于读写逻辑的问题,一开始是在收到CK_WRITE的时候更新【源文件】偏移量,在收到CK_READ时更新【目的文件】的偏移量,而且发现网上也有这么做的,后来经过折腾发现逻辑有点问题,反过来比较合理,

即 收到CK_WRITE时更新【目的文件偏移量】,收到CK_READ时更新【源文件偏移量】。

5、执行结果

【windows核心编程】IO完成端口(IOCP)复制文件小例的更多相关文章

  1. 【windows核心编程】IO完成端口(IOCP)复制文件小例前简单说明

    1.关于IOCP IOCP即IO完成端口,是一种高伸缩高效率的异步IO方式,一个设备或文件与一个IO完成端口相关联,当文件或设备的异步IO操作完成的时候,去IO完成端口的[完成队列]取一项,根据完成键 ...

  2. 【Windows核心编程】一个使用内存映射文件进行进程间通信的例子

    进程间通信的方式有很多种,其底层原理使用的都是内存映射文件. 本文实现了Windows核心编程第五版475页上的demo,即使用内存映射文件来在进程间通信. 进程1 按钮[Create  mappin ...

  3. 《windows核心编程》 17章 内存映射文件

    内存映射文件主要用于以下三种情况: 系统使用内存映射文件载入并运行exe和dll,这大量节省了页交换文件的空间以及应用程序的启动时间 开发人员可以使用内存映射文件来访问磁盘上的数据文件.这使得我们可以 ...

  4. Windows核心编程 第十七章 -内存映射文件(下)

    17.3 使用内存映射文件 若要使用内存映射文件,必须执行下列操作步骤: 1) 创建或打开一个文件内核对象,该对象用于标识磁盘上你想用作内存映射文件的文件. 2) 创建一个文件映射内核对象,告诉系统该 ...

  5. Windows核心编程 第十七章 -内存映射文件(上)

    第1 7章 内存映射文件 对文件进行操作几乎是所有应用程序都必须进行的,并且这常常是人们争论的一个问题.应用程序究竟是应该打开文件,读取文件并关闭文件,还是打开文件,然后使用一种缓冲算法,从文件的各个 ...

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

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

  7. 《Windows核心编程》之“完成端口”(对所有IO都是如此,不仅仅是对socket)

    <Windows核心编程>第10章开头部分一再强调:“IO Completion Port”是“构建高性能.可升缩的应用程序”的最佳设施之一,它不仅适用于处理设备IO,也适用于其它越来越多 ...

  8. windows核心编程---第九章 同步设备IO与异步设备IO之同步IO

    同步设备IO 所谓同步IO是指线程在发起IO请求后会被挂起,IO完成后继续执行. 异步IO是指:线程发起IO请求后并不会挂起而是继续执行.IO完毕后会得到设备的通知.而IO完成端口就是实现这种通知的很 ...

  9. 《Windows核心编程》读书笔记 上

    [C++]<Windows核心编程>读书笔记 这篇笔记是我在读<Windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对 ...

随机推荐

  1. Project Euler 97 :Large non-Mersenne prime 非梅森大素数

    Large non-Mersenne prime The first known prime found to exceed one million digits was discovered in ...

  2. POJ1182 食物链

    并查集经典题1. 向量的思考模式2. 再计算向量时,要画图:有一个关系一开始写错了3. 本人的norm函数一开始x >= 3写成了 x>3,应该对这种小函数多做UT(口头上的,比如)4. ...

  3. 前台将勾选的多个属性放到一个value里面,是一个字符串,传到后台

    jq function changeStreet(a){ var valk=$(a).html(); $(a).parents(".select_box").children(&q ...

  4. ssm框架整合小结

    1.整合思路 一.Dao层:整合mybatis和spring 需要的jar包: 1.mybatis的jar包 2.Mysql数据库驱动 3.数据库连接池 4.Mybatis和spring的整合包. 5 ...

  5. OpenVAS漏洞扫描基础教程之OpenVAS概述及安装及配置OpenVAS服务

    OpenVAS漏洞扫描基础教程之OpenVAS概述及安装及配置OpenVAS服务   1.  OpenVAS基础知识 OpenVAS(Open Vulnerability Assessment Sys ...

  6. SPOJ 1108 Card Trick 暴力模拟

    解释一下样例,因为我觉得这个题意表述的不是很清楚.以第二组样例为例. 牌序为:3 1 4 5 2 第一轮:把 3 放到末尾:1 4 5 2 3,最顶上的牌是1,把1拿走.剩余 4 5 2 3 第二轮: ...

  7. smartcomb:用php实现的web模块拼合器

    smartcomb是一个用php实现的web模块拼合器,相对于其他的代码拼合工具,如下特性: 可以拼合任意类型的文件,不限于js文件. 集中并声明依赖,自动分析依赖拼合,按需加载. 支持多种配置切换 ...

  8. Entity Framework学习 - 1.连接数据库

    1.添加项,选择ADO.NET实体数据模型 2.选择从数据库生成 3.建立数据库连接 4.选择要关联的表 点击完成,数据库已连接完毕,并生成了对应的Model

  9. Drawit插件

    gvim用不了画矩形的功能,只能在vim下用 \di,\ds开始/结束画图(Vim里\按键没有被映射,可以做leader按键) 用鼠标选择一块之后,\b画矩形,\e画椭圆 选单行\a画箭头,\l画线 ...

  10. struct inode 和 struct file

    1.struct inode──字符设备驱动相关的重要结构介绍 内核中用inode结构表示具体的文件,而用file结构表示打开的文件描述符.Linux2.6.27内核中,inode结构体具体定义如下: ...