- 浏览: 2053107 次
- 性别:
- 来自: 北京
-
文章分类
- 全部博客 (651)
- ACE (35)
- BAT (9)
- C/C++ (116)
- fast-cgi (14)
- COM (27)
- python (59)
- CGI (4)
- C# (2)
- VC (84)
- DataBase (29)
- Linux (96)
- P2P (6)
- PHP (15)
- Web (6)
- Memcached (7)
- IME输入法 (11)
- 设计模式 (2)
- 搜索引擎 (1)
- 个人情感 (4)
- 笔试/面试 (3)
- 一亩三分地 (33)
- 历史 (2)
- 地理 (1)
- 人物 (3)
- 经济 (0)
- 不仅仅是笑哦 (43)
- 小故事大道理 (2)
- http://www.bjdsmyysjk120.com/ (0)
- http://www.bjdsmyy120.com/ (0)
- 它山之石可以攻玉 (15)
- 大学生你关注些什么 (28)
- 数据恢复 (1)
最新评论
-
luokaichuang:
这个规范里还是没有让我明白当浏览器上传文件时,STDIN的消息 ...
FastCGI规范 -
effort_fan:
好文章!学习了,谢谢分享!
com技术简介 -
vcell:
有错误os.walk(strPath)返回的已经是全部的文件和 ...
通过python获取目录的大小 -
feifeigd:
feifeigd 写道注意:文章中的CPP示例第二行 #inc ...
ATL入门:利用ATL编写简单的COM组件 -
feifeigd:
注意:文章中的CPP示例第二行 #include " ...
ATL入门:利用ATL编写简单的COM组件
http://www.blogcn.com/user8/flier_lu/index.html?id=2401390&run=.0D9CAA6
完成端口是 NT 架构下一种高效的异步 IO 辅助机制,其使用方法已经被广为讨论,MSDN里面也有很详细的说明和示例。《Windows网络编程》一书中有关于通过完成端口实现高效网络服务器设计的详细说明;《Windows核心编程》一书中有关于异步IO以及线程池等相关知识的使用介绍;而《Windows 2000 内部揭密 》一书中则有对完成端口实现原理的简要介绍;此外一些关于 Win32 下多线程编程方面的书籍也对完成端口有所提及。
因此,对于完成端口的使用我这里就不再罗嗦,下面将直接从实现角度对其进行分析。
首先,从设计角度来看:完成端口是一个 NT 执行子系统的核心对象。通过将完成端口与任意个 I/O 句柄(文件或Socket等)关联,使得用户可以通过完成端口这一统一途径,异步的获取并处理 I/O 操作的结果,同时能够最大限度利用多 CPU 的优势。而且因为完成端口实际上是 NT 系统很多底层机制如 APC 的实现手段,故而在效率上是最高的,因为绝大多数其他类似解决方法最终还是使用完成端口实现。与 WaitForMultipleObjects 函数不同,完成端口是由系统直接提供并行优化支持的,通过建立完成端口时指定的并行线程值,系统可以保证工作在同一完成端口上的线程数量受控(一般等于系统CPU数量),这样就可以避免无意义的线程上下文切换,获取更高的性能。
其次,从使用角度来看:完成端口使用 CreateIoCompletionPort 函数和 CloseHandle 函数创建和释放,使用 GetQueuedCompletionStatus 函数和 PostQueuedCompletionStatus 函数实现完成端口的读写操作。因此我们的分析从这三个函数开始。
CreateIoCompletionPort 函数在 ExistingCompletionPort 参数为空的时候调用 NtCreateIoCompletion 函数(iocomplete.c:51)创建一个新的完成端口对象;然后处理 FileHandle 参数为 INVALID_HANDLE_VALUE 时的情况;最后调用 NtSetInformationFile 函数(dll estrfil.c:740)试图将 I/O 句柄绑定到完成端口上。伪代码如下:
完成端口是 NT 架构下一种高效的异步 IO 辅助机制,其使用方法已经被广为讨论,MSDN里面也有很详细的说明和示例。《Windows网络编程》一书中有关于通过完成端口实现高效网络服务器设计的详细说明;《Windows核心编程》一书中有关于异步IO以及线程池等相关知识的使用介绍;而《Windows 2000 内部揭密 》一书中则有对完成端口实现原理的简要介绍;此外一些关于 Win32 下多线程编程方面的书籍也对完成端口有所提及。
因此,对于完成端口的使用我这里就不再罗嗦,下面将直接从实现角度对其进行分析。
首先,从设计角度来看:完成端口是一个 NT 执行子系统的核心对象。通过将完成端口与任意个 I/O 句柄(文件或Socket等)关联,使得用户可以通过完成端口这一统一途径,异步的获取并处理 I/O 操作的结果,同时能够最大限度利用多 CPU 的优势。而且因为完成端口实际上是 NT 系统很多底层机制如 APC 的实现手段,故而在效率上是最高的,因为绝大多数其他类似解决方法最终还是使用完成端口实现。与 WaitForMultipleObjects 函数不同,完成端口是由系统直接提供并行优化支持的,通过建立完成端口时指定的并行线程值,系统可以保证工作在同一完成端口上的线程数量受控(一般等于系统CPU数量),这样就可以避免无意义的线程上下文切换,获取更高的性能。
其次,从使用角度来看:完成端口使用 CreateIoCompletionPort 函数和 CloseHandle 函数创建和释放,使用 GetQueuedCompletionStatus 函数和 PostQueuedCompletionStatus 函数实现完成端口的读写操作。因此我们的分析从这三个函数开始。
CreateIoCompletionPort 函数在 ExistingCompletionPort 参数为空的时候调用 NtCreateIoCompletion 函数(iocomplete.c:51)创建一个新的完成端口对象;然后处理 FileHandle 参数为 INVALID_HANDLE_VALUE 时的情况;最后调用 NtSetInformationFile 函数(dll estrfil.c:740)试图将 I/O 句柄绑定到完成端口上。伪代码如下:
以下为引用:
#define IO_COMPLETION_ALL_ACCESS 0x001F0003
typedef enum _FILE_INFORMATION_CLASS
{
//...
FileCompletionInformation = 30,
//...
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
typedef struct _IO_STATUS_BLOCK
{
union {
NTSTATUS Status;
PVOID Pointer;
};
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
HANDLE CreateIoCompletionPort(
HANDLE FileHandle,
HANDLE ExistingCompletionPort,
ULONG_PTR CompletionKey,
DWORD NumberOfConcurrentThreads)
{
HANDLE CompletionPort = ExistingCompletionPort;
// 创建一个新的完成端口对象
if(ExistingCompletionPort == NULL)
{
NTSTATUS st = NtCreateIoCompletion(&CompletionPort, IO_COMPLETION_ALL_ACCESS, NULL, NumberOfConcurrentThreads);
if(!NT_SUCCESS(st))
{
SetLastNTError(RtlNtStatusToDosError(st));
return NULL;
}
}
if(FileHandle == INVALID_HANDLE_VALUE)
{
// FileHandle 参数为 INVALID_HANDLE_VALUE 时 ExistingCompletionPort 参数必须为空
if(ExistingCompletionPort != NULL)
{
CompletionPort = NULL;
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
}
// 只创建完成端口对象,但并不绑定 I/O 句柄到完成端口上
return CompletionPort;
}
IO_STATUS_BLOCK blk;
// 将 I/O 句柄绑定到完成端口上
NTSTATUS st = NtSetInformationFile(FileHandle, &blk, &CompletionKey, 8, FileCompletionInformation);
if(NT_SUCCESS(st))
{
return CompletionPort;
}
else
{
SetLastNTError(RtlNtStatusToDosError(st));
if(CompletionPort)
{
CloseHandle(CompletionPort);
}
return NULL;
}
}
CloseHandle 在处理完成端口时则直接调用 IopDeleteIoCompletion 函数(iocomplete.c:675)。而 GetQueuedCompletionStatus 函数和 PostQueuedCompletionStatus 函数,则分别是 NtRemoveIoCompletion 函数(iocomplete.c:485)和 NtSetIoCompletion 函数(iocomplete.c:417)的简单包装。
以下为引用:
BOOL GetQueuedCompletionStatus(HANDLE CompletionPort, LPDWORD lpNumberOfBytes,
PULONG_PTR lpCompletionKey, LPOVERLAPPED* lpOverlapped, DWORD dwMilliseconds)
{
LARGE_INTEGER Timeout;
BaseFormatTimeOut(&Timeout, dwMilliseconds);
IO_STATUS_BLOCK blk;
NTSTATUS st = NtRemoveIoCompletion(CompletionPort, lpCompletionKey, &dwMilliseconds, &blk, &Timeout);
if(FAILED(st) || st == STATUS_TIMEOUT)
{
// 设置错误代码
}
else
{
if(SUCCEEDED(blk.Status))
{
// 设置 lpOverlapped 和 lpNumberOfBytes
}
}
}
从实现角度来看,完成端口实际上是一个 IoCompletionObjectType 类型的内核对象,其对象体就是一个 KQUEUE 结构维护的内核队列。创建此内核对象类型信息的代码如下(io/ioinit.c:1527):
以下为引用:
//
// Create the object type for I/O completion objects.
//
RtlInitUnicodeString( &nameString, L"IoCompletion" ); // 对象名称
objectTypeInitializer.DefaultNonPagedPoolCharge = sizeof( KQUEUE ); // 对象体
objectTypeInitializer.InvalidAttributes = OBJ_PERMANENT | OBJ_OPENLINK;
objectTypeInitializer.GenericMapping = IopCompletionMapping;
objectTypeInitializer.ValidAccessMask = IO_COMPLETION_ALL_ACCESS;
objectTypeInitializer.DeleteProcedure = IopDeleteIoCompletion; // CloseHandle 调用函数
if (!NT_SUCCESS( ObCreateObjectType( &nameString, &objectTypeInitializer,
(PSECURITY_DESCRIPTOR) NULL, &IoCompletionObjectType )))
{
return FALSE;
}
NtCreateIoCompletion 函数(iocomplete.c:51)只是简单地构造并初始化这个内核对象。
首先对来自用户态的调用验证 IoCompletionHandle 参数指向内存可写;然后调用 ObCreateObject 函数构造内核对象;如果成功则调用 KeInitializeQueue 函数初始化内核队列;并且调用 ObInsertObject 函数将内核对象加入到当前进程的句柄表中;最后将构造的内核对象的句柄返回给调用者。伪代码如下:
以下为引用:
NTSTATUS
NtCreateIoCompletion (
IN PHANDLE IoCompletionHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN ULONG Count OPTIONAL)
{
NTSTATUS st;
try
{
// 对来自用户态的调用验证 IoCompletionHandle 参数指向内存可写
if(KeGetPreviousMode() != KernelMode)
{
ProbeForWriteHandle(IoCompletionHandle);
}
PVOID IoCompletion; // 完成端口内核对象
// 构造内核对象
st = ObCreateObject(..., IoCompletionObjectType, ..., sizeof(KQUEUE), ..., &IoCompletion);
if(NT_SUCCESS(st))
{
// 初始化内核队列
KeInitializeQueue((PKQUEUE)IoCompletion, Count);
HANDLE Handle;
// 将内核对象加入到当前进程的句柄表中
st = ObInsertObject(IoCompletion, ..., &Handle);
if(NT_SUCCESS(st))
{
try
{
// 将构造的内核对象的句柄返回给调用者
*IoCompletionHandle = Handle;
}
catch(...)
{
// 完成端口内核对象已创建并插入当前进程句柄表,因此需要忽略错误
}
}
}
}
catch(...)
{
st = GetExceptionCode();
}
return st;
}
系统还提供了一个相应的 NtOpenIoCompletion 函数(iocomplete.c:178)支持打开已经建立的有名字的完成端口。其调用 ObOpenObjectByName 函数完成名字到内核对象的映射,并最终将完成端口对象句柄返回给调用者。伪代码如下:
以下为引用:
NTSTATUS
NtOpenIoCompletion (
OUT PHANDLE IoCompletionHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes)
{
NTSTATUS st;
try
{
// 对来自用户态的调用验证 IoCompletionHandle 参数指向内存可写
if(KeGetPreviousMode() != KernelMode)
{
ProbeForWriteHandle(IoCompletionHandle);
}
HANDLE Handle;
st = ObOpenObjectByName(ObjectAttributes, IoCompletionObjectType, ..., DesiredAccess, ..., &Handle);
if(NT_SUCCESS(st))
{
try
{
// 将构造的内核对象的句柄返回给调用者
*IoCompletionHandle = Handle;
}
catch(...)
{
}
}
}
catch(...)
{
st = GetExceptionCode();
}
return st;
}
在建立了完成端口对象后,可以通过 NtQueryIoCompletion 函数(iocomplete.c:282)查询此完成端口队列中阻塞的 I/O 完成包的数量。此函数实际上是对读取内核队列状态 KeReadStateQueue 函数(kequeueobj.c:92)的封装,从 KQUEUE::Header::SignalState 字段读取完成端口内部使用的内核队列对象的阻塞深度。
NtRemoveIoCompletion 函数(iocomplete.c:485)和 NtSetIoCompletion 函数(iocomplete.c:417) 实际上也只是直接调用内核队列维护函数 KeRemoveQueue 函数(kequeueobj.c:238) 和 KeRundownQueue 函数(kequeueobj.c:566) 完成对内核队列的操作。
在构造和初始化完成端口内核对象后,需要开始对完成端口进行操作,以实现对完成端口获取或发送完成包的操作。而这些操作归根结底实际上都是对内核队列进行操作,因此得先对内核队列有所了解。我会单独写一篇 BLog 讨论内核对象的实现思路,这里暂且不深入讨论。
... to be continue ...
发表评论
-
__declspec(novtable) 的用法
2010-11-27 14:37 1611__declspec(novtable) 的用法 __d ... -
解决URLDownloadToFile缓存问题的两种方法
2010-09-09 15:18 2977解决URLDownloadToFile缓存问题的两种方法 ... -
修改richedit背景
2010-07-19 22:52 1681RichEditCtrl::SetBackgroundCo ... -
使用ADO封装类的数据库程序开发实例(下)
2010-07-12 15:30 1514使用ADO封装类的数据库 ... -
使用ADO封装类的数据库程序开发实例(上)
2010-07-12 15:28 1243使用ADO封装类的数据库 ... -
VC防止窗口和控件闪烁的方法
2010-07-09 21:16 20611、将Invalidate()替换为Invalidate ... -
防止窗口闪烁地办法
2010-07-09 21:13 1557防止窗口闪烁地办法 也许我们都碰到过这种情况,当你 ... -
使用ADO _ConnectionPtr
2010-07-06 16:04 5290// GetUser.cpp : Defines the ... -
VC用ADO访问数据库全攻略
2010-07-06 15:29 1829VC用ADO访问数据库全 ... -
深入GetMessage和PeekMessage (引自-MSDN技术组)
2010-06-10 16:59 3780深入GetMessage和PeekMessage (引自 ... -
界面编程总结(1)
2010-06-02 13:32 4056原文地址:http://blog.csdn.net/byx ... -
获取信息的有关Windows API
2010-05-27 10:01 3186获取信息的有关Windows API 1.窗口信息 ... -
VC中如何实现窗口的隐藏
2010-05-13 10:08 7908VC中如何实现窗口的隐藏 用MFC做的Dialog ... -
SetConsoleCtrlHandler 处理控制台消息
2010-05-07 17:32 18223SetConsoleCtrlHandler 处理控制台消 ... -
解决决错误: error C2850: 'PCH header file'
2010-04-27 19:45 1999解决决错误: error C2850: 'PCH hea ... -
VC++ GDI+编程的字体和文本绘制
2010-04-13 13:12 8012字体是文字显示和打印的外观形式,它包括了文字的字样、风格和尺寸 ... -
VC利用GDI+显示透明的PNG图片
2010-04-12 16:59 115971.在你将要使用GDI+的工程中,完成初始化 ... -
GDI+编程基础(一)GDI+ Vs GDI
2010-04-12 15:59 2377下载源代码一、GDI GDI是位于应用程序与不同硬件之间 ... -
VC画图
2010-04-12 15:50 1582BOOL DrawPic(HDC hdc, TCHAR* ... -
对话框的数据交换--MFC深入浅出
2010-04-12 10:43 2508对话框数据交换指以下两种动作,或者是把内存数据写入对应的控 ...
相关推荐
1. 完成端口(Completion Port)通知:这是VxD常用的一种方法,通过创建一个完成端口并关联到设备句柄,当设备完成I/O操作时,系统会自动将一个消息放入完成端口队列中,应用程序可以通过GetQueuedCompletionStatus...
标题中的“IOCP.rar”指的是一个关于IOCP(Input/Output Completion Port,输入/输出完成端口)技术的压缩文件,主要针对使用Delphi编程语言的开发者。IOCP是Windows操作系统提供的一种高度优化的异步I/O模型,常...
嵌入式八股文面试题库资料知识宝典-华为的面试试题.zip
训练导控系统设计.pdf
嵌入式八股文面试题库资料知识宝典-网络编程.zip
人脸转正GAN模型的高效压缩.pdf
少儿编程scratch项目源代码文件案例素材-几何冲刺 转瞬即逝.zip
少儿编程scratch项目源代码文件案例素材-鸡蛋.zip
嵌入式系统_USB设备枚举与HID通信_CH559单片机USB主机键盘鼠标复合设备控制_基于CH559单片机的USB主机模式设备枚举与键盘鼠标数据收发系统支持复合设备识别与HID
嵌入式八股文面试题库资料知识宝典-linux常见面试题.zip
面向智慧工地的压力机在线数据的预警应用开发.pdf
基于Unity3D的鱼类运动行为可视化研究.pdf
少儿编程scratch项目源代码文件案例素材-霍格沃茨魔法学校.zip
少儿编程scratch项目源代码文件案例素材-金币冲刺.zip
内容概要:本文深入探讨了HarmonyOS编译构建子系统的作用及其技术细节。作为鸿蒙操作系统背后的关键技术之一,编译构建子系统通过GN和Ninja工具实现了高效的源代码到机器代码的转换,确保了系统的稳定性和性能优化。该系统不仅支持多系统版本构建、芯片厂商定制,还具备强大的调试与维护能力。其高效编译速度、灵活性和可扩展性使其在华为设备和其他智能终端中发挥了重要作用。文章还比较了HarmonyOS编译构建子系统与安卓和iOS编译系统的异同,并展望了其未来的发展趋势和技术演进方向。; 适合人群:对操作系统底层技术感兴趣的开发者、工程师和技术爱好者。; 使用场景及目标:①了解HarmonyOS编译构建子系统的基本概念和工作原理;②掌握其在不同设备上的应用和优化策略;③对比HarmonyOS与安卓、iOS编译系统的差异;④探索其未来发展方向和技术演进路径。; 其他说明:本文详细介绍了HarmonyOS编译构建子系统的架构设计、核心功能和实际应用案例,强调了其在万物互联时代的重要性和潜力。阅读时建议重点关注编译构建子系统的独特优势及其对鸿蒙生态系统的深远影响。
嵌入式八股文面试题库资料知识宝典-奇虎360 2015校园招聘C++研发工程师笔试题.zip
嵌入式八股文面试题库资料知识宝典-腾讯2014校园招聘C语言笔试题(附答案).zip
双种群变异策略改进RWCE算法优化换热网络.pdf
内容概要:本文详细介绍了基于瞬时无功功率理论的三电平有源电力滤波器(APF)仿真研究。主要内容涵盖并联型APF的工作原理、三相三电平NPC结构、谐波检测方法(ipiq)、双闭环控制策略(电压外环+电流内环PI控制)以及SVPWM矢量调制技术。仿真结果显示,在APF投入前后,电网电流THD从21.9%降至3.77%,显著提高了电能质量。 适用人群:从事电力系统研究、电力电子技术开发的专业人士,尤其是对有源电力滤波器及其仿真感兴趣的工程师和技术人员。 使用场景及目标:适用于需要解决电力系统中谐波污染和无功补偿问题的研究项目。目标是通过仿真验证APF的有效性和可行性,优化电力系统的电能质量。 其他说明:文中提到的仿真模型涉及多个关键模块,如三相交流电压模块、非线性负载、信号采集模块、LC滤波器模块等,这些模块的设计和协同工作对于实现良好的谐波抑制和无功补偿至关重要。
内容概要:本文探讨了在工业自动化和物联网交汇背景下,构建OPC DA转MQTT网关软件的需求及其具体实现方法。文中详细介绍了如何利用Python编程语言及相关库(如OpenOPC用于读取OPC DA数据,paho-mqtt用于MQTT消息传递),完成从OPC DA数据解析、格式转换到最终通过MQTT协议发布数据的关键步骤。此外,还讨论了针对不良网络环境下数据传输优化措施以及后续测试验证过程。 适合人群:从事工业自动化系统集成、物联网项目开发的技术人员,特别是那些希望提升跨协议数据交换能力的专业人士。 使用场景及目标:适用于需要在不同通信协议间建立高效稳定的数据通道的应用场合,比如制造业生产线监控、远程设备管理等。主要目的是克服传统有线网络限制,实现在不稳定无线网络条件下仍能保持良好性能的数据传输。 其他说明:文中提供了具体的代码片段帮助理解整个流程,并强调了实际部署过程中可能遇到的问题及解决方案。