基于 IOCP 机制的负控管理系统研究

时间:2022-08-28 05:57:43

基于 IOCP 机制的负控管理系统研究

【摘 要】针对电力负控管理系统的客户端连接数量多、数据接收和发送频繁的现状,提出一种基于 I/O完成端口( IOCP: I/O completion port) 机制的大规模负控管理系统体系结构。研究了引入 IOCP 机制的通信模块的基本原理、实现方案和关键问题解决方法。

【关键词】大规模电力管理系统;负控软件;完成端口

1 负控管理系统体系结构

负控管理系统是一个着眼于全面加强电力信息管理的,集负荷控制、远程抄表、电量数据分析和监测以及电力营销管理等多种功能于一体的综合性分析与处理系统。负控管理系统分为 3 层结构: 主站层、通信层和采集设备层,其系统结构如图 1 所示。在大规模的负控管理系统中,具有众多的远程采集终端设备和 C/S 客户端,远程采集终端负责采集海量的设备信息,并定时上送至主站。C/S 客户端进行日常电费收缴、用户查询、报表打印等工作,并能实现对用户电表的管理与控制功能,其并发访问服务器的概率较大。这都对服务器的网络通信性能提出了较高要求。为此,在软件开发中,通过在网络通信模块中引入 IOCP 机制,以优化软件系统中服务器的网络通信性能,进而增强系统的稳定性。

2 IOCP 网络通信模块

2.1 I /O 完成端口

IOCP 是一个通知队列,操作系统把已经完成的重叠 I/O 请求消息放入其中,某项 I/O 操作一旦完成,对该操作结果进行处理的工作者线程就会收到通知,完成对操作的后续处理。API 函数CreateIoCompletionPort 将一个 I / O 完成端口与一个或多个客户端套接字发生关联。当与一个完成端口相关的套接字启动的异步 I/O 操作完成时,相应的 I/O 完成包就会进入到该 I/O 完成端口队列中; 对于多个套接字来说,此机制可以将多个I / O 的 完 成 包 放 入 单 个 队 列 中。 API 函 数GetQueuedCompletionStatus 将线程与某个指定的I / O 完成端口建立联系,此线程会阻塞它的运行于完成端口,直至有 I/O 完成包进入到完成端口队列中。客户端套接字在被创建后,可以在任何时候与 I/O 完成端口进行关联。完成端口通信模型充分利用内核对象的调度,只使用少量的几个线程来处理和客户端的所有通信,消除了无谓的线程上/下文切换,最大限度地提高了网络通信的性能。I /O 完成端口最重要的特性是并发量。该并发量限制了与该完成端口相关联的可运行线程数目,当与该完成端口相关联的可运行线程数目达到了该并发量,系统就会阻塞任何与该完成端口。

2.2 IOCP 通信模块工作流程

客户端的连入一般采用 Accept 方式,而采用AcceptEx 方式可以使性能进一步优化。采用 Ac-ceptEx 方式的工作流程如图 2 所示。传统的 Accept 函数接收客户端时,Accept 函数需要创建一个新的 socket 作为返回值,分配给接入的客户端; 客户端断开连接时,创建的 socket会被销毁。创建 socket 是一个浪费系统资源的操作; 如果短时间内并发连接的客户端不多的话,用Accept 和 AcceptEx 性能上区别不大,但是当并发数目增多时,如果使用 Accept 方式,系统会出现WSAENOBUFS 问题,这是由于系统不能及时为客户端创建 socket 造成的。而 AcceptEx 函数把事先创建好的 socket 对象,分配给接收到的客户端,其原理为: 首先创建好一定数量的 socket 对象,形成一个连接池,当接收到客户端的连接请求时,从连接池中取出空闲的 socket 对象,分配给该客户端,断开连接时,再将 socket 放回连接队列。连接池的设计减少了客户端 socket 的不断创建与销毁,节省了大量的系统资源。

2.3 IOCP 通信模块难点解析

IOCP 机制虽然具有诸多优点,但实际使用中需要解决许多棘手的问题,如信息定位、内存资源管理、恶意客户连接及完成包排序等。在大规模负控管理系统中客户端并发量大,远程终端设备定时上传数据期间数据量巨大,GPRS 连接线路掉线频繁,这都使得上述问题尤为明显。

2.3.1 信息定位

在大规模负控管理系统中,必定有大量的远程终端和客户端并发访问服务器,如何在工作线程中获取从完成端口中读取的 I/O 完成包相关的客户端信息以及数据信息是一个难点。函数 GetQueuedCompletionStatus 传递给工作者线程的参数中具有两个“伸缩”作用的参数,一个是指向“完成键”( CompletionKey) 的 PULONG_PTR 类型指针 lpCompletionKey,它所指向的“完成键”其实是函数 CreateIoCompletionPort( 在客户端套接字与完成端口相关联时调用) 传入的 Com-pletionKey 参数,此参数可以是自定义的结构体;另一个是指向 Overlapped 结构体类型的指针,它其实是指向在投递 I/O 请求时传入的 Overlapped结构体的参数,操作者可以充分利用这两个参数完成信息定位。定义两个结构体: 一个是单 I/O 数据结构体PER_IO_CONTEXT,每一个 I / O 请求对应一个单I / O 数据,里面有用来存储数据的数据缓冲区参数和标识操作类型的参数; 另一个是单句柄数据结构体 PER_SOCKET_CONTEXT,用来管理每个客户端 socket 的上下文相关信息。

2.3.2 内存资源管理

内存管理问题是网络通信模块的关键问题。系统资源是有限的,大量并发 I/O 请求时,很容易出现内存不足、资源访问紊乱等问题,必须对系统资源管理进行合理的设计。在大量客户端并发访问服务器时,会执行大量的异步接受和异步发送操作,这些操作大都暂时被悬挂起来,直到服务器端 I/O 重叠请求准备好为止,每个异步操作都需要使用页面缓存块,这些页面块加载到内存被加锁,使它们不会被交换到页面文件中。但是操作系统对锁定内存的容量有限 制,达 到 限 制 时,异 步 I/O 操 作 就 会 以WSAENOBUFS 错误返回。在负控管理系统中,异步发送问题不明显,避免在主线程中直接执行写命令,让其同样在工作者线程中执行。异步读取的简单解决办法是在连接上投递一个零字节缓冲区请求,这样就不会有内存被锁定。零字节的投递操作返回表示有网络数据可读取时,再投递一个非零字节缓冲区操作进行异步读取。在 X86的体系中,内存页面是以 4KB 为单位锁定的,为了避免内存浪费,重叠操作缓冲区的大小选为页大小 4KB 的倍数,这可以减少页面锁的总容量。

2.3.3 完成包排序问题

调 用 函 数 GetQueuedCompletionStatus 获 取I / O完成端口队列中的完成包请求时,是以先入先出的 方 式 进 行 的,但 是 唤 醒 那 些 调 用 函 数GetQueuedCompletionStatus 的线程是以后入先出的方式进行的,通常情况下,这不会对数据通信造成影响,但对数据包顺序有要求,如在大容量数据传输时,线程调度问题可能会导致关联到完成端口的实际操作不按正常顺序完成。目前通用的方法是在每个 Socket 的“单句柄数据”上增加两个互斥变量分别来标识当前缓冲区中数据包读、写的顺序,同时对包含的“单 I/O数据”增设重叠 I/O 请求的顺序号。在发送数据时,如果当前发送缓冲区数据包的顺序号与 I/O 操作号相等,说明请求的数据即是现在要发送的数据,否则先缓存这个数据,等到它前面的数据包被处理后再继续处理该数据,作也采取同样的策略。

3 负控管理系统上层软件架构

负控管理软件采用分布式多层结构,软件架构分为表现层、应用层、服务层和数据层,如图 4所示。

表现层____提供统一的业务应用操作界面和信息展示窗口,是系统直接面向操作用户的部分。业务层____实现具体业务逻辑,是系统主站的核心层,根据系统的应用特点,业务层可分为采集子层、业务子层、对外接口等。服务层____提供全局通用的业务服务、安全服务等组件服务支持,并实现本系统专用的业务逻辑服务,为业务层提供通用的技术支撑。数据层____实现海量信息的存储、访问、整理,为系统提供数据的管理支持。汇集服务器是负控管理系统正常运转的关键,需要全天候、无人值守的运行,完成与远程终端设备间的数据通信任务,并将抄读回的数据进行解析,将电能信息存入数据库等。通过汇集服务器也可以实时了解各远程终端设备的状态。

4 结束语

完成端口机制的引入,并利用各种优化措施( 如客户端采用AcceptEx方式接入,各种“池”的使用等) ,对通信模块性能进行了优化,对 IOCP开发过程中的信息定位、内存资源管理、恶意客户连接以及完成包排序等关键问题进行了有效解决,使负控管理系统的软件网络通信整体性能得到了极大的提高( 尤其在 CPU 占用) ,使软件系统能够承载与众多客户端并发通信。

参考文献:

[1]王艳平,张越.WINDOWS网络与通信程序设计[M].上海: 人民邮电出版社,2006.

[2]刘暾东,黄祚,孙宏飞.完成端口技术在路灯监控软件中的应用[J].照明工程学报,2010(2).

上一篇:公路路基施工技术现状分析 下一篇:配电网中架空绝缘导线的应用