网络I/O

2024-08-01

网络I/O(精选8篇)

网络I/O 篇1

1 概述

目前我们网络所面临的依然是高并发的问题,就像某些电商网站双11时的情况,瞬间的并发量是惊人的,当然我们会有很多种方法去解决这个问题,本文我们谈论的是单台服务器,如何提高自己对并发请求的处理能力。要想解决这个问题,我们需要先理清楚Unix和类Unix系统的I/O模型。

IO也就是输入输出即读写操作,在操作系统内部逻辑上一般会分两个空间(实际是内存映射):用户空间和内核空间。为了保证数据的安全性,只有内核才有权限从物理存储(或网络数据)上直接进行读写操作,而我们的服务进程都是处于用户空间的,所以说一次IO操作就被分为了两个阶段:1)内核从磁盘上读取数据到内核空间;2)复制内核空间中的数据到用户空间。了解这个之后我们就可以讲解下5种不同的IO模型。

2 IO模型原理详述

2.1 阻塞I/O(Blocking I/O)

阻塞IO是最早期也是原理最简单的I/O模式,应用进程发出一个I/O请求,该I/O操作不能立刻完成,它需要等待内核将数据读取出来,再等待从内核空间中将数据拷贝出来,这两个环节进行完之前,应用进程都处于阻塞状态。

2.2 非阻塞I/O(Non-blocking I/O)

应用进程向内核发送一个I/O请求,如果没有可用数据,内核会向用户返回一个错误值,用户会在将来的一个合适的时候再次进行请求操作,这样就避免了进程的阻塞,这种往返的操作也被称为轮询。这里有一点需要注意的是,在Copy data from kernel user的时候进程依然是被阻塞的,之所以叫做非阻塞是因为只有在内核读写数据的阶段(上文的第一阶段)才叫做IO操作,所以在需要有大量进程并发请求时非阻塞IO模式并不比阻塞IO模式效率高,甚至更加浪费资源。

2.3 多路复用I/O(I/O Multiplexing)

多路复用IO在Linux系统中一般只用于网络IO,非阻塞IO中的用户进程轮训会消耗大量的系统资源,多路复用的提出就是将这种轮训方式通过select或poll系统调用来完成,使用select或poll操作,进程可在多个套接字上等待网络事件,当其中某个套接字发生某个网络事件时,用户可通过查看网络事件对该套接字进行I/O操作。但当所有套接字都没有网络事件发生时,进程还会阻塞起来。所以,多路复用I/O模型本质上还是基于阻塞I/O的。

2.4 信号驱动式I/O(Single-driven I/O)

信号驱动式IO也有叫事件驱动式IO,这种IO具有以上三种IO都不具有的优势,即当用户进程发起IO请求后即可离开,当内核将数据准备好后,将想用户进程发送一个信号以通知其来拷贝数据,第一阶段用户进程时完全的非阻塞的也不需要进行轮训,只有在第二阶段用户进程才是阻塞状态。但在多进程或多线程服务器中,信号驱动I/O存在一定问题,即信号在产生和传递到目的进程之间,状态标志可能会发生变化。

2.5 异步I/O(Asynchronous I/O)

用户进程提出IO请求后即可做其他的操作,当数据准备好之后,内核会直接通知用户进程I/O操作的结果。这种机制容易和信号驱动I/O混淆,两者的不同是:异步I/O返回时,I/O操作已经完成,返回的是I/O操作的结果;信号驱动I/O只是通知进程可以开始进行I/O操作,进程得到这个信号后才开始I/O操作。

3 五种不同I/O模型的比较和适用场景

3.1 阻塞模型

客户端连接至服务器端的每一个专用进程,服务器端的进程处理这些连接数据。如果有大量用户请求连接,服务器端就建立大量线程或者进程,线程或进程间的切换会使系统性能大大下降。这种模型适合服务器负载不大或并发数不多的情况。

3.2 非阻塞I/O

此种模式主要应用在单进程服务器中。当服务器进程接受到请求后,该进程会收到内核返回的一个错误值,此进程可以去做其他操作,单该进程并不知道何时再次进行I/O请求操作,所以只能通过轮询的方法查询连接状态,但是轮询操作会造成CPU的浪费。

3.3 多路复用I/O

此模型是对上诉两种模型的改进,进程可在多个描述符上等待网络I/O事件,担当没有任何网络事件发生时,进程会进入阻塞状态。这种模型一般使用select/poll进行操作,进程在多个socket上进行监听,如果有IO事件发生,CPU需要扫描所有活动链接,已处理有数据传输的链接,并且单个进程可监视的fd数量被限制,即能监听端口的大小有限。因此,该模型适合多并发连接的情况,且这些并发连接大多要有IO事件发生。

3.4 信号驱动I/O

信号驱动I/O模型用得比较少,主要使用在UDP套接字上,TCP套接字几乎不使用。这是因为,在UDP编程中使用信号驱动I/O,此时SIGIO信号产生于下面两种情况:1.套接字收到一个数据报。2.套接字上发生了异步错误。因此,当应用因为收到一个UDP数据报而产生的SIGIO时,要么可以调用recvfrom读取该数据报,要么得到一个异步错误。而对于TCP编程,信号驱动I/O就没有太大意义了,因为对于流式套接字而言,有很多情况都可以导致SIGIO产生,而应用又无法区分是什么具体情况导致该信号产生的。例如:1)监听套接字完成了一个连接请求。2)收到了一个断连请求。3)断连操作完成。4)套接字收到数据。5)有数据从套接字发出。对于TCP下应用信号驱动I/O模型,我们应该考虑只对“监听TCPsocket”使用SIGIO,因为对于“监听TCPsocket”产生SIGIO的唯一条件是新连接完成。也就是说,只有TCP下只有用作listen的端口,才考虑使用信号驱动I/O模型。

3.5 异步I/O

Linux下的异步I/O模型使用的非常少,通常认为Linux下没有比较完美的异步文件I/O方案。目前比较出名的有Glibc的AIO与Kernel Native AIO,当有大量异步IO请求时,将会产生大量用户级线程,这些线程的切换将会极大的影响系统性能,所以AIO适用于并发访问量并不大的应用。

3.6 epoll模型

是针对poll模型的改进。它没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口);不采用轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;即Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。并且epoll利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。

4 结束语

从上述的IO模型中我们发现在现如今常见的大量活跃并发的场景中,只有epoll模型的效率是最高的,只有在一种极其极端的条件下,即所有活动链接都有数据传输时select/poll模型比epoll模型效率稍高。epoll模型通过callback方法实现系统异步通知,只返回活跃的进程,减少了轮训的时间,并通过mmap节省了复制的开销。

参考文献

[1]Gary.wrigh W.Richard Stevens.TCP/IP详解(卷1):协议[M].北京:机械工业出版社,2007.

[2]滕昱.2.6内核中提高I/O性能的新方法——epo1l[z].(2005—03~30).http://mechgouki.spaces.1ive,com/blog/Personal-Space.aspx.

[3]Gary.wrigh W.Richard Stevens.TCP/IP详解(卷2):协议[M].北京:机械工业出版社,2010.

网络I/O 篇2

raw_input

input

raw_input函数

raw_input([prompt]) 函数从标准输入读取一个行,并返回一个字符串(去掉结尾的换行符):

#!/usr/bin/python str = raw_input(“Enter your input: ”);print “Received input is : ”, str

这将提示你输入任意字符串,然后在屏幕上显示相同的字符串。当我输入“Hello Python!”,它的输出如下:

Enter your input: Hello PythonReceived input is : Hello Python

input函数

input([prompt]) 函数和raw_input([prompt]) 函数基本可以互换,但是input会假设你的输入是一个有效的Python表达式,并返回运算结果。

#!/usr/bin/python str = input(“Enter your input: ”);print “Received input is : ”, str

这会产生如下的对应着输入的结果:

网络I/O 篇3

云计算的数据交付模式是以打散的物理硬件组成新的大型资源池,以不同的应用来索取不同的资源,达到按需交付。它改变了传统数据计算方式,是计算模式的一种变革。数据中心的云计算化,让每个数据中心内部的资源能够最大化的利用,并达到按照不同用户的请求,进行从底层资源到操作系统再到上层应用的一整套架构模式。

云计算数据中心资源池化的应用,使得每个物理主机的资源能够最大化利用,进而减少物理节点数量。这样不仅节省很多空间、机房、电力成本,还减少诸如布线、维护、网络交换机成本等等。如今,很多行业都是多个云计算数据中心,如果能够将多云数据中心内部资源全部形成一个大型的、统一的、灵活的、集中的池化空间,那么资源利用率、用户应用的响应效率、投资回报率等都会得到显著提升。

但是网络I/O虚拟化问题,成为云计算平台高效应用交付平台的一个瓶颈[1]。由于网络系统与云计算之间关联性比较弱,以至于现今阶段云计算出现一个网络处理性能弱、服务器利用效率高的现象[2]。虽然网络系统中交换机自身处理性能已经很高,但是因虚拟资源的无法度量性、变化性、不可见性使得网络系统无法能够真正地将虚拟资源与网络系统融合[3,4]。数据包聚合算法可以使得网络I/O虚拟化与云进行高度集成,使得这个瓶颈得以解决。国内外有很多学者已经研究了云中网络I/O虚拟化问题[5,6],但是其主要还是从云计算、并行计算、分布式角度提高网络I/O,本文针对从软件算法调度数据报文角度提高了网络I/O虚拟化。

1 本地网络I/O虚拟化

云平台基本上由多个数据中心门户网站,通过广域网连接[7]。数据中心是由多个物理组成的节点通过局域网连接。在数据中心虚拟化架构中,每个物理机器可以虚拟多个孤立虚拟机。这些虚拟机共享相同的硬件和存储。虚拟化依赖于虚拟机监视器(VMM)或虚拟机监控程序支持多个虚拟机器共享同一物理机器硬件资源并确保隔离。在X86架构中,Xen是一个很受欢迎的开源VMM确保虚拟机与网络堆栈之间的隔离。在Xen环境下,虚拟机被作为无特权的域被交付,当作Domain U。一个特殊的特权域(假使Domain0)负责管理一系列的虚拟机的创建,迁移等[8]。接入网络设备是以共享的形式,通过一个特殊的虚拟机称为驱动域来访问网络并共享资源。驱动域拥有真正的设备驱动程序,并且可以很直接地访问设备。除了真实设备驱动外,虚拟的驱动也会在驱动域和虚拟机处被实施。一个虚拟的驱动被分成两部分:netback(驱动域里),netfront(虚拟域里)。

图1中显示了在虚拟化系统中,数据包的数据流流向。当一个数据包将要到网络设备时(a),网络设备会即刻通过物理中断的形式通知虚拟机监控程序(b)。监控程序会通过事件通道作为一个虚拟中断来转发此通知到驱动域(c)。然后,驱动域的设备处理此数据包并把它转到以太网桥(d)。然后根据它的目的MAC地址,把其交付到响应的netback(e)。netback通过事件通道来通知netfront(f),告知数据包的到达并且将其复制到它的地址空间(h)。Netfront看见此消息(g),继而会寻找共享内存内的数据包并把其交给其上层处理(i)。数据包出方向的数据流也是如此,流程相反。

2 联网性能评估

2.1 测试平台

网络的数据流是通过一个PCI X4通道的千兆卡所处理。E1000驱动使能NAPI。我们用半虚拟化模式的Xen 3.4版本。首先,虚拟两个特殊的虚拟机:Domain0和一个驱动域。并且新建4个客户机用于流量处理,传输并转发。所有虚拟机运行IA-64 Linux内核。Domain0运行2.4.60版本而驱动域和其他客户机运行2.6.24版本。驱动域被分配四核,并且每个虚拟机仅仅被分配单核。随着流量的发起,用4个IBM X系列服务器(2核2.4 MHz主频,1 GB DDR2内存,1个千兆NIC),每服务器运行IA-64 Linux(版本号:2.6.24)内核。

在上述情形当中,虚拟机作为路由和交换,把数据包发送到其目的MAC和IP地址,并接收数据包,以相同的形式处理。在流量下降时测得其最大转发率。

2.2 实验结果

虚拟机常常在传输数据包上比接收数据包上拥有较高的吞吐量。这主要是因为在接收数据包时还得执行三个操作。这些操作包含:首先,客户机发送I/O缓存页表授权I/O通道。然后,在接收到数据包后,驱动域通过一种超级调用来通知管理程序。最后,它就会复制数据包到客户也表。然而,在传输数据包时,却没有数据包的复制过程,进而也就没有管理程序的通告了。驱动域仅仅映射数据包到其地址空间。

2.3 硬件瓶颈分析:内存延时

内存延时是指CPU在一个给定的内存位置中读写的时间。假使,真实内存时钟为333 Mhz,时钟周期持续3 ns并且内存延时在5个与30个周期之间构成。其具体周期取决于数据的位置和其长度。最好的情形相对于仅仅列地址选通脉冲延时中,它花费的时间在处理器请求内存一些数据时与其之后的返回之间(5个周期)。而相对于最差的情形需要额外的25个周期(5+5+15)。第一个5个周期代表了从行到列地址选通脉冲的延时。第二个5个周期代表了行地址选通脉冲的预充电,此时,它花费在禁用一行数据的访问与访问另一行数据的开始。最后的15个周期代表预充电延时:内存不得不等待很长时间知道再次访问时已经被初始化。

2.4 软件瓶颈分析

为了克服由于内存延时带来的瓶颈,我们发现软件组件最有助于内存处理来减少内存延时。因此,针对一个数据包的转发路径,我们把软件组件分为五大函数类:

e1000:物理设备驱动。

桥:驱动域内数据包多路复用和分解函数的集合。

I/O:通过事件通道和内存,驱动域和虚拟机之间的传输处理数据包的函数。

Xen_D0:管理程序和Domain0时序安排的函数和管理虚机并保护对内存的访问。

VM:已经正常运转的虚拟机。

图2描述了随着速率的增加,以上五类各自内存处理的情况。我们首先可以看到当输入速率超过80 Kp/s时,完成内存处理的总数停滞在15 MT/s。根据前面瓶颈分析过程,我们可以发现这个结果证明了内存延时作为瓶颈的问题。再进一步说,我们可以发现超过50%的内存处理都是通过I/O函数来实现的。为了使网络能够更快地提高输入速率,我们深入研究下这些函数。这些函数包含了转发操作。这些操作很可能需要最多的内存处理。表1列出了这些函数。我们发现以下列出的函数产生了67.59%的内存处理,并且只有物理驱动(Phycial_Driver)需要唤醒在驱动域与虚拟机间传输数据的I/O通信。它们分表代表了驱动域所要求的授权来访问虚拟机的内存页(gnttab_copy,gnttab_end_foreign),复制数据包过程中的缓存分配(alloc_skb),复制数据包(memcpy),并通知netfront。其中的一些诸如spin_lock_irqsave等,都为基本linux内核函数,但与处理过程关联甚密,以此列出。这类函数是针对每个数据包需要在netfront和netback之间处理时被唤醒的。

3 基于I/O虚拟化的数据包聚合

3.1 包聚合和提取算法

由于数据报文在排队过程中形成的容器导致了一定流量延时,因此多个数据包应打破容器形成模式,在构成容器前进入。但对于非实时流量,排队包引起的延时就不是什么问题。当然各个不同流量需要保持一定的延时。然后,每个容器用超时的方式来阻止不可承受的延迟。这种超时在容器形成之时,根据在数据流在延时方面的需求来初始化延时。每个容器都关联一个最大值V。它的默认值是在驱动域和每个虚拟机之间的被分享的内存页的大小。默认内存页的大小是4 096字节。包聚合算法如算法1、算法2。

如上描述,包聚合是基于目的MAC地址的。尽管如此,上述被提出的机制是针对基于包的目的IP地址和VLAN标签来创建容器的。

C代表Container容器,P代表Packet数据包。

算法1包聚合

算法2 Create_C(P)函数

算法3包提取

3.2 聚合性能评估

3.2.1 情形

图3显示了在以聚合为基础的系统,一个数据包的转发路径。随着数据包从NIC的接收(Polldevice),然后数据包就被聚合(进容器container),然后发送给连接到虚拟网卡的Guest域(发送模块To Device),数据包在Guest域被提取(Unloder)。

3.2.2 延时和抖动

提高吞吐量很明显能够减少数据包丢失率。在输入速率达到80 Kp/s之时,丢失率会超过90%。在3.2.1节所述的情形下,我们评估了数据包到达网络设备时与离开系统时的时间差。我们描绘了数据包的平均延时并让输入速率函数作为其最大的抖动值。这种抖动值的计算方法是通过RFC1889中提到的方法计算的。然而这种抖动值也能很好地展示出这种聚合系统在延时变化的影响。相对于同一个容器中的两个相邻的数据包而言,一个容器的最后一个包和来自下一个容器的第一个包的延时是很高的。很明显,大容器能够获得很高的吞吐量,然而却会以更大的超时所带来的延时作为代价。

3.2.3 内存和CPU消耗

图4、图5和图6、图7分别借助接收与转发数据包的状态与输入速率与内存处理的状态,来显示非聚合系统与聚合系统的内存状态、CPU状态。我们发现,当接收/转发吞吐量大幅度增加时,对于数据包的接收和转发,聚合系统中驱动域内消耗CPU的周期会大大降低,而且每个包的总内存消耗也降低了。例如,在内存状态图表中,在数据包转发的情况下,在400 Kp/s的输入速率,它已经从每个数据包30 MT下降到10 MT。注意,一个虚拟机最大转发吞吐量分别是非以聚合为基础的系统的60 Kp/s和以聚合为基础系统的800 Kp/s。

4 包聚合标注工具

4.1 假设

当数据包从驱动被传递到虚拟机的过程中,容器会接受到一个参数为u0的指数服务。当他们从虚拟机VMi,i=1,2,…,N,传回到驱动域时,容器会接受到一个参数为ui,i=1,2,…,N的指数服务。这个假设简化了系统模型并且导致了真实系统下的主要行为。甚至很可能通过一个M/M/1序列演变为一个M/M/D序列参考文献[1]。

4.2 共享内存的数据包到达过程的特性描述

引用文献[2]中,所表示的参数为(ai,Ti,i=1,2,…,N)的N PH重合处理过程。当然,也保存了相位式分布(a,T)。

其中:

以此类推:

代表克罗内克积,Ii为单位矢量,并且(ai,Ti*)是Erlang进程(λi,ki)的残留期,(ai,Ti*)的表示如下:

这个矩阵用G来表示。在产生矩阵分析方法的理论当中,它与R*同样扮演一个重要的角色。这个关联矩阵G是非线性矩阵方程的最小非负解。

对于数值求解的最近的一些算法,涉及先计算G然后计算基于关系矩阵的R*:

5 结语

本文通过包聚合的方式,来提高云计算中的网络I/O虚拟化。通过多种方式证明了聚合方法与非聚合方法在云计算中网络I/O虚拟化的不同表现形式以及资源的利用率。通过本文的这种聚合系统的方式,可以很好地解决云计算环境中网络I/O虚拟化对于CPU和内存的占用模式,进而提高云计算环境中的数据包经过网络系统时I/O的效率,更大地节省CPU和内存资源,以更加高效的方式进行云计算环境下网络I/O虚拟化的数据交付模式。

参考文献

[1]玄文启.基于云计算的虚拟化技术原理与应用分析[J].中国科技信息,2012(23):78-78.

[2]Armbrust M,Fox A,Griffith R,et al.A view of cloud computing[J].Communications of the ACM,2010,53(4):50-58.

[3]Menon A,Cox A L,Zwaenepoel W.Optimizing network virtualization in Xen[C]//USENIX Annual Technical Conference,2006:15-28.

[4]Raj H,Schwan K.High performance and scalable I/O virtualization via self-virtualized devices[C]//Proceedings of the 16th international symposium on High performance distributed computing.ACM,2007:179-188.

[5]Gao M.Survey of Current Progress of Cloud Computing and Its Interoperability[J].Software Engineering and Applications,2013(2):20-25.

[6]Hwang K,Fox G C,Dongarra J,et al.云计算与分布式系统:从并行处理到物联网[M].机械工业出版社,2013.

[7]喻摇波,胡怀湘.MR-IOV:下一代数据中心I/O虚拟化技术[J].计算机技术,2013(10):97-100.

关于I/O模拟的讨论 篇4

多年以来,Windows都为这种机制而引以为豪,其实在Windows下,有这样的一个原则,硬件例如CPU最好是一直工作的,这样才能得到更加充分的利用,而在Windows下,程序都是以进程形式获得在CPU这类的硬件中执行的机会,而作为轻量级的进程,执行资源的实际获得者线程的角度上看,就是要让线程不要停下来,需要让各个线程就它们正在执行的操作进行相互的通信,Microsoft在这个领域进行了数十年的研究和测试,终于开发了这种机制,叫做I/O完成端口(I/O completion port),通过这种机制,就可以让线程在读取设备或写入设备的时候不必挂起等待设备的响应,从而很大程度上提高了吞吐量,实现了计算机性能的巨大提升,如图1所示。

首先分析一下Windows的消息机制,只有了解了Windows的消息机制才能明白,什么是消息,Windows下利用上述的I/O机制,实现了线程与I/O之间的交互不必需要线程的挂起,但是消息产生到接收又是怎么解决的呢?Windows感知到有I/O的操作以后,会将该消息进行封装成为struct tagMSG(详细内容请查阅MSDN),这个结构体中将包含消息的类型,参数,时间,位置,和传递的对象。Windows将该消息投入到Windows消息队列,然后等待应用程序从队列中提取相对应的消息,在这里要明确几个问题,便是模拟I/O的层次问题和模拟的关键是什么?正如图1表达的,模拟可以在驱动级,系统级,或者用户级3个级别上进行,关键就在于模拟的何种消息、何时、何位置、发送给那些应用程序,这就成为了I/O模拟的关键所在。

2 用户级I/O模拟

正如图1所示,从消息队列到指定应用程序的这一段路径上的I/O模拟,便是用户级模拟,用户级模拟的特点是:

(1)通过Windows消息机制,并由系统发出,给指定的应用程序。

(2)指定的应用程序通过自己的窗口句柄等标识,从消息队列中获取消息。

这一部分的模拟需要用到Windows API,首先了解到在Windows下的应用程序,都会拥有一个自己的窗口句柄,这如同一个ID一样,指向了某一个窗口程序,就通过获取到指定应用程序的句柄,然后发送特定消息,这如何来实现呢,分享一段模拟代码:

这里就不再赘述一些细节了,但是需要注意的是,在FindWindowsEx中的第二参数,解决一些存在父子窗口的情况,通过对子窗口的句柄,有点类似线程ID了,就可以向指定线程发送消息,按键的消息需要延迟200毫秒,这是因为正常人的点击操作便是200毫秒,这样首先可以避免写入消息队列过快,造成溢出,也可以满足一些需要一定按键时长程序的特殊要求。这里的PostMessage函数有时可以用SendMessage函数替代,本身参数上没有区别,但是核心在于如果使用的是PostMessage那么发出消息后,模拟程序是不会理会该消息的处理的,但是使用SendMessage的话,应用程序就会一直等待该消息被正确处理的反馈,这样会一定程度上影响模拟程序的正常运行,因此建议使用PostMessage函数。其次Windows的消息叫做Windows_Message,这类消息共有1024(0x0400),所以可以设置高于1024的自定义消息。这样就可以模拟很多Windows下无法模拟的I/O消息,但是弊端就是要为自定义消息编写处理函数。

3 系统级I/O模拟

这一部分分析操作系统到消息队列这一段路径上的I/O模拟,这一段的模拟所具有的特点是:

(1)系统并不指定发送的对象,接受消息的进程为当前的活动进程。

(2)接收消息的程序可以发生改变,换句话说是可以存在进程间切换的。

首先分析一下正常情况下键盘消息的产生过程,这好比要完成对某一好友的问候,那么应当先选中一个好友,然后对他发送你好的消息,这一过程就是平时使用鼠标点击选中当前活动进程,或是用键盘切换选择活动进程的过程。这一部分的模拟通过Windows API也是可以实现的,下面我们分享一段模拟代码:

键盘消息的模拟,需要讨论的是虚键码MapVirtualKey函数,这个函数把逻辑上的‘U’键转换成计算机可以识别的二进制虚键码,可以不使用这个函数,但是效果就是模拟出的键盘消息不会被一些DirectX类游戏所接受,鼠标事件的模拟就要显得相对复杂了,因为不仅要产生事件消息,还要产生位置消息,这里的案例是对640*480窗口向全屏的鼠标坐标转换,有点类似窗口压缩的意思,需要解释的是对于一些DirectX类的射击游戏的鼠标模拟,这套方案并不是首选方案,其原因会在下面的驱动级模拟中详细给大家分析。

4 驱动级I/O模拟

这一部分要分析从驱动到操作系统和一种特殊的消息传递方式,在这之前,先要了解这样一个事实,在应用程序中有一类游戏应用程序叫做DirectX类游戏,比如冰封王座,CS,街霸,铁拳等,这类游戏的一个共同特征是:

(1)需要很高的即时操作要求,而且操作频繁。

(2)不使用Windows的消息机制,而是与驱动直接交互。

首先为了很好的人机交互感,这类游戏都会将操作精确到毫秒级,而且会产生大量的操作,这就要求对I/O缓存的一定使用要求,其次为了实现这类游戏的即时要求,这类游戏都会选择绕过Windows消息机制,因为通过Windows消息机制至少都有数百毫秒的延时,这难以满足游戏的需要,为此目的,这类游戏会选择直接与驱动进行交互,来接受I/O的操作。在这个层面的模拟就会显得比较复杂,而且Windows的API就不太好用了。

因此在这个层面的I/O模拟有两种设计方案,一种是需要重写某些I/O设备的驱动,这是比较麻烦的,还有一种方法是将消息写入某个特定的驱动然后产生相应的消息,下面着重分享一下第二种方法,写入驱动程序:

使用写入驱动的接口是WinI/O提供的,通过WinI/O就可以和驱动进行一定程度上的交互,分析一下这几段关键性代码:首先KBCWait4IBE()函数是用来检测驱动的缓存是否为空的,因为开始讨论到,如果模拟的消息过平凡地投递到驱动,那么消息就会出现一定程度上的溢出,这样的话,很多的操作就会出现丢失,这样是很不好的,所以每当要将内容写入到驱动的时候,都要检测一下是否驱动的缓存已经满了,这并不会造成严重的延迟,但是对一些组合操作来讲,丢失一个操作就有可能丢失了一批操作,所以在驱动级模拟I/O的操作关键就在于避免缓存溢出。此外,MY_KEY_DOWN&MY_KEY_UP是相类似的,将差异分析一下,首先,要产生的按键的ASCII码转换为虚键码,然后等待缓存,当可以写入的时候首先要发送一个命令消息0xD2,即为写入缓存,然后得到写入的权限后写入一个数据0xE2,告诉驱动要写入的是一个键盘消息,然后等待缓存,写入要写入消息的虚键码,于是消息被写入,但是这个时候键盘消息是按下消息,如何将其按起呢?使用0x80也就是标识当前某一按键被取消的标识位,当这一位被标识后,按键消息就变成了按键取消消息,从而从键盘上驱动模拟发出这一消息。

5 结语

详细介绍了在用户级、系统级和驱动级实现对I/O设备的模拟操作,分享了Windows API编程、WinIO驱动编程的案例。也详细地分析了Windows消息机制的优缺之处和如何利用和脱离Windows消息机制。

摘要:详细分析Windows的I/O机制,提出了分别在用户级、系统级、驱动级的I/O模拟操作,通过实际案例和源代码分享在I/O模拟上的得失,着重介绍鼠标与键盘的模拟。

关键词:I/O机制,I/O模拟

参考文献

对象I/O技术的模拟实现 篇5

我们知道C++对象是“存活”在RAM中的,由于RAM的易失性[1],程序需要将对象存入磁盘中,将来需要时再把对象读入内存加以恢复,这样一来就好像对象一直“活”着一样,因此对象的持久化是C++中的一个非常重要的操作。

许多程序员可能有这样的误区,利用下面的代码:saveClassName();className=readClassName();p=new className;不就轻松实现对象的保存和恢复了吗?

需要指出的是,在C++中,是通过new A而非new“A”(或className=“A”,new className)实例化A的对象。换句话说,试图利用下面的代码className=readClassName();p=new className;来达到通过类的字符串名称动态创建对象的做法是根本行不通的,因为p=new className根本无法通过编译![2]

由此我们得出对象的持久化需要的两个条件:

(1)获取对象所属类的名称的能力;

(2)能根据类的字符串名字动态创建对象的能力。

这两种能力的获得目前有两种解决方案:

一是由C++编译器(compiler)提供———例如Borland C++4.5:

二是由程序员自己加上去。

本文是通过第二种方法模拟实现对象的持久化机制,从而深入探讨了对象I/O技术的实现机制。

本文结构如下,首先描述了对象持久化的实现,其次对实现进行了验证,最后是论文进行总结。

1 对象持久化的实现

为了说明问题而又不失一般性,我们假定有三个类,分别是Object,A和B,其中A和B都派生于Object,类的定义如下:

为了让对象具备持久化的两个条件,需要依次为对象添加如下信息。

1.1 为每个类增加对象创建函数CreatObject,如表2所示。

1.2 增加类的识别信息(类名称或ID等)以及继承信息,由于这部分信息比较多,可以将其整合到一个结构体Struct ClassInfo中。ClassInfo中保存有两个链表:

类的继承链表和程序中所有类的类型信息链表。类的识别能力就由这两个链表来完成。换句话说,我们希望在main函数执行之前内存中就存在如图1所示的两个链表。其中类的继承链表是由Struct ClassInfo的带参构造函数完成的(在VC++中,结构体也可以有构造函数),而类的类型信息链表则是由ClassInfoInit类完成的。Struct ClassInfo和ClassInfoInit的定义如表3所示。注意,链表的创建是在它们的构造函数中完成的。

最后一步,将类型信息作为类的静态的成员变量添加进来,并为每个类实例化静态的初始化类,目的是在main函数执行之前得到图1所示的两个链表。

在这要强调一下static关键字的作用:

(1)如果类的成员变量被Static关键字所修饰,则这该属性不是为类中的每个对象分别拥有,而是共用,其引用形式不是对象成员变量,而是类成员变量,即不需要市里实例化对象就可以使用。成员函数也与此相仿[3]。

(2)如果对象(包括作为类成员的对象),如initObject、ObjectInfo等,被说明为Static,则这些对象是在main函数执行之前就已经存在了,换句话说,这些对象的构造函数是在main函数调用之前就已经被调用了。因此在main函数执行之前,内存中就已经存在着图1所示的链表就可以理解了。

我们添加的RTTI信息能否有效的支持对象的持久化可以由试验来验证。

2 试验

该试验主要验证了对象持久化必须具备的两个能力,即动态创建对象能力和获取对象所属类的能力。试验的环境如下:操作系统Windows XP sp2,IDE环境是VC++6.0 SP6。如需要全部源代码可与作者联系(hehh6@henu.edu.cn)。

通过对象增加的RTTI信息(增加的静态成员变量),获取对象所属类的能力自然具备。

动态创建对象能力实际上就是根据类的字符串名称来实例化对象的能力,在图1所示链表的支持下,该功能可以非常轻松的实现。

思路如下:通过ClassInfo::pFirstClass查找类名匹配的ClassInfo,利用ClassInfo中的pCreateObject指针实例化对象,为方便起见,可为程序增加查找函数

3 结束语

本文在手动添加的RTTI信息的支持下实现了对象的持久化,从中读者可以深入了解对象持久化的的实现机制。

在商业的编译器VC++中其实现持久化是通过一个神秘的宏,当将该宏展开后,VC++实现持久化的的思路和本文大同小异,只是更精巧[4]。

参考文献

[1]Stanley B.Lippman.Inside The C++Object Model(深度探索C++对象模型)[M].侯捷,译.武汉:华中科技大学出版社,2001.

[2]葛磊.The Object's Permanence Technology and Its Realization in MFC[J].开封大学学报,2006,20(1).

[3]钱能.C++程序设计教程[M].第二版.北京:清华大学出版社,2005:418-419.

远程I/O系统在罐区的应用 篇6

关键词:远程I/O,通信,DCS

0 引言

金陵石化芳烃部中间罐区仪表采用远程RTU接入DCS方式, 现场检测、报警仪表的信号接入远程I/O单元, 由远程I/O单元转换成通信信号, 连接至相应的DCS系统, 控制仪表及可燃气有毒气检测仪表采用电缆直连至相应的DCS系统。

现场每套远程I/O系统输出两路Modbus信号, 分别进入两个不同装置的DCS系统, 一路进PX装置, 另一路进3重整装置。

1 远程I/O系统

模块化的远程I/O系统通过总线接口将现场二进制和模拟量的传感器和执行器连接至控制系统, 传输安全区域或者爆炸危险区域的现场过程数据。远程I/O系统可连接设备:流量变送器、阀门定位器、压力传感器或者温度变送器、热电偶 (TCs) 、电阻式温度检测器 (RTDs) 、机械触点以及声光报警器等。远程I/O系统兼容Profibus-DP、Modbus-RTU、Modbus TCP/IP和Foundation Fieldbus等网络协议。远程I/O适用于爆炸危险区或者严酷工业环境, 例如海洋平台和户外。典型的应用行业包括石油天然气 (内陆及海洋) 、制药、化工、污水处理、食品饮料等。

远程I/O系统具备高适应性和操作简单的特征。系统安装在现场设备侧, 仅需一根总线电缆即可连接远程I/O系统到DCS, 能够减少接线, 还能够节省时间、节约成本和减少工作量。

P+F公司的LB系列远程I/O具备危险区2区认证, 可安装在Zone 2、Class I、Div.2或者非危险区域。该模块化的信号调节系统将现场信号关联至非危险区域的控制系统, 连接方式有安全区域现场连接 (NonEx) 和本质安全区域现场连接 (Ex i) 。

金陵石化公司芳烃部新建中间罐区正是采用LB系列的远程I/O系统, 如图1所示。

该系统的底板结构如图2所示。X4为现场总线接口, 用来与DCS通信, 现场实际用于与PX通信;X5为冗余现场总线接口, 用来与DCS通信, 现场实际用于与#3重整通信;X6为服务总线, 用于连接PC机进行相应的组态;X7为扩展机架接口;CHANNEL1/2为通信模块;CHANNEL3-24为I/O卡件;POWER1-3插槽为插相应的供电模块。

2 远程I/O系统的组态过程

(1) 打开PACTWARE, 添加设备, 如图3所示。

(2) 选择添加相应的LB系统, 选择好相应的COM口后选择SCAN SERVICE BUS, 自动扫描网关设备, 如图4所示。

(3) 按照设计图纸在每个通道插入相应的插件, 然后进行具体的点组态, 如图5所示。

(4) 在实际的组态中, 为了通信方便, 处理量程统一设置成0~4095。为了能够与TPS系统通信, 必须对相应的Modbus地址进行分配, 如图6所示。点击Auto marshalling按钮, 系统会自动根据卡件的通道号分配相应的地址。组态结束后, 对相应的系统进行下载。

3 与DCS通信

LB远程I/O系统与Honeywell TPS系统采用的是标准的Modbus-RTU通信协议。

控制器和主机为主从关系。仅一设备 (主设备) 能初始化传输 (查询) , 其它设备 (从设备) 根据主设备查询提供的数据做出相应反应。这种传输方式也使得信号在Modbus网络中的通信井然有序。

根据系统的特点, 采用RS-485接口, 接口采用平衡驱动器和差分接收器的组合, 使用屏蔽双绞线传输 (一般只需二根连线) 数据, 抗共模干扰能力增强, 抗噪声干扰性好。

将现场远程I/O系统的通信线连接DCS中的SI卡FTA板, 在DCS中组态相应的ARRAY点, 设置地址、波特率、通信协议, 分别如图7、图8、图9所示。

建立相应的通信之后, 要对模拟量点的量程进行转换, 以达到实际使用要求。

4 结语

远程I/O系统不但节省成本, 减少工作量, 而且可以在线对参数进行修改, 在线监测功能强大, 利于故障的判断, 减少了维护量。

参考文献

[1]丁宝苍.过程控制系统与装置[M].重庆:重庆大学出版社, 2013

[2]乐嘉谦.仪表工手册[M].北京:化学工业出版社, 2003

[3]厉玉鸣.化工仪表及自动化[M].北京:化学工业出版社, 2011

[4]张春华, 肖体兵, 李迪.工程测试技术基础[M].北京:机械工业出版社, 2011

[5]王树青, 乐嘉谦.自动化与仪表工程师手册[M].北京:化学工业出版社, 2010

[6]武平丽, 高国光.过程控制工程实施[M].北京:电子工业出版社, 2011

CC2530普通I/O口的扩展 篇7

1 CC2530的I/O口扩展方法的总体思路及各组成工作原理

1.1 CC2530的I/O口扩展方法的总体思路

本扩展方法采用CC2530的I/O口模拟IIC总线的SCL和SDA, 通过IIC总线控制PCA9554芯片, 使串行通信变成并行通信。从理论上来讲CC2530芯片P1口的8个I/O口就可以连接4组IIC通信, 每组因扩展芯片地址A2A1A0的不同可同时支持8个芯片, 每个芯片又可控制8个端口。因此, CC2530的P1口从理论上就可支持128个 (4×8×8) 端口, 如图1所示。并且, 如果芯片固定的头地址不一样的话, 例如PCA9554和PCA9554A, 那么端口数量又可增加一倍, 这样P1口就扩展成了256个端。依此类推CC2530的I/O口通过扩展数量呈几十倍的增加, 可大大丰富I/O口的应用。

1.2 IIC总线工作原理

IIC总线只有两条线, 一条是数据线SDA, 另一条是时钟线SCL。数据线SDA在时钟线SCL的控制下可串行发送和接收数据, 传送速率最高达100kbps。各被控制器件均并联在这两条线上, 每个接到IIC总线上的器件都有唯一的地址, 通过不同地址决定通信对象, 如图1所示。这样, 各控制电路虽然挂在同一条总线上, 却彼此独立, 互不相关。IIC总线的优点是占用的空间非常小, 可避免线路互连而使电路板走线错综复杂的现象, 减少了电路板上的布线空间, 降低了互联成本。

IIC总线上只有四种信号:开始信号、停止信号、重新开始信号和应答信号。IIC总线上能实现主/从双向通讯, 器件发送数据到总线上, 则定义为发送器, 器件接收数据则定义为接收器。主器件和从器件都可以工作于接收和发送状态。IIC总线时序如图2所示。

1.3 PCA9554工作原理

PCA9554是带中断的GPIO扩展芯片, 它提供了IIC总线方式应用中的8位串行转并行输入/输出口的扩展。PCA9554共有4条命令, 在总线主控制器 (本文中为CC2530) 向PCA9554发送写数据过程中, 命令字节是紧跟地址字节, 之后第一个字节作为一个指针指向要进行写或读操作的寄存器。CC2530通过总线写I/O口的相应配置位来激活端口的输入或输出。通过给PCA9554的A2A1A03个管脚不同的电平来实现不同IIC地址, A2A1A03个管脚可组成8个地址码, 由此总线上最多允许挂8个PCA9554。而PCA9554与PCA9554A的IIC固定地址不同 (PCA9554的固定地址为01000A2A1A0, PCA9554的固定地址为01110A2A1A0) , 这样就允许16个器件 (PCA9554和PCA9554A各8个) 连接到同一个IIC总线上, 每个器件扩展出8个I/O端口, 这样CC2530的两个I/O端口就可扩展到8x16个I/O口。

2 扩展I/O口的应用

本应用通过CC2530的I/O口 (P1.0和P1.1) 模拟IIC总线的SCL和SDA, 然后通过IIC总线形式控制GPIO扩展芯片PCA9554, 最后通过扩展的IO来控制LED、蜂鸣器、按键信号、继电器等, 如图3、4所示。

3 结语

本文通过IIC总线形式控制GPIO扩展芯片PCA9554扩展CC2530的I/O口。在文主要提出了具体的硬件设计, 并运用于实践中。CC2530的I/O口进行扩展, 使I/O口的数量大大增多, 能丰富开发设计者的运用。

参考文献

[1]施邦平.采用I2C器件扩展单片机的多级通信[J].自动化与仪器仪表, 自动化与仪器仪表, 2006 (6) :63-64.

网络I/O 篇8

关键词:I/O性能,流,缓冲区,分配和回收

Java应用程序都会经常使用java.io包, 例如磁盘文件读写和通过网络传输数据。然而初学者由于对java.io包的理解的局限性导致编写的程序I/O性能很差。所幸的是, 只要对java.io包有了很好的理解, 就可以杜绝这方面的问题, 从而保证程序拥有较好的I/O性能。

本文首先对java.io包做一个简要的介绍, 然后指出导致I/O性能低下的根本原因是没有采用缓冲机制, 然后循序渐进地采取了三种策略, 将I/O性能逐步提高到168倍, 这三种策略分别是采用缓冲流, 定制自己的缓冲区, 定制静态的固定长度的缓冲区。

1 I/O操作的基本概念

在Java I/O中最基本的概念是流 (Stream) , 流是一个连续的字节序列, 包括输入流和输出流, 输入流用来读取这个序列, 而输出流则用来写这个序列, 在默认情况下Java的流操作是基于字节的, 即一次只读或只写一个字节。

java.io包提供了Input Stream和Output Stream作为对I/O操作的抽象, 这两个接口决定了类层次结构的基本格局。Input Stream和Output Stream的具体实现类提供了对不同数据源的访问, 如磁盘文件和网络连接。

java.io包还提供了过滤流 (Filter Stream) , 过滤流并不指向具体的数据源, 而是在其他流之上进行了包装, 这些过滤流其实是java.io包的核心。从性能的角度来看, 最重要的过滤流是缓冲流 (Buffered Stream) , 图1显示了一个简化的类层次结构图。

2 缓冲流

导致I/O性能低下的主要原因是没有对I/O操作进行缓冲。众所周知, 硬盘擅长于大块数据的读写, 但是在小量数据的读写上性能不好, 所以, 为了最大化I/O性能, 我们应该选择批量的数据操作, 而缓冲流正是为这个目的设计的。缓冲流, 包括Buffered Input Stream和Buffered Output Stream, 它为I/O流增加了内存缓冲区, 使得Java程序一次可以向底层设备写入或者读取大量数据, 从而提高了程序的性能。

为了更好地理解缓冲流的效果, 请阅读程序清单一, 这个例子采用了原始的文件流实现文件的拷贝。copy方法打开了一个File Input Stream和一个File Output Stream, 并将数据从一个流直接拷贝到另一个流。由于read和write方法是基于字节的, 所以实际的磁盘读写也是按字节发生的。实践证明使用这段代码拷贝一个8M的文本文件需要花费36750ms。

程序清单一中的copy方法只需稍作修改就能有效地改善性能, 如程序清单二所示, 它在原始的文件流之上采用了缓冲流Buffered Input Stream和Buffered Output Stream, 缓冲流将每一个小的读写请求积攒起来, 然后一次性地批处理, 通常是将几千个读写请求合并成一个大的请求。改进后的copy方法拷贝一个8M的文本文件需要花费1187ms, 性能大约提高了31倍。

3 建立自己的缓冲

缓冲流虽然在它的内部增加了内存缓冲区, 使得在缓冲区和底层设备之间写入或读取大量数据成为可能[2,3], 但是在这之上Java程序仍旧使用while循环从该流 (实际上是缓冲数组) 按字节读写数据。由于缓冲数组的大小是一定的, JVM需要针对数组做越界检查, 该操作会导致额外的系统开销。另一方面, 为了支持多线程环境, 将数据从Buffered Input Stream的缓冲数组拷贝到Buffered Output Stream的缓冲数组, 其中的方法调用很多都是synchronized, 这也会导致额外的系统开销。所以尽管程序清单二所示代码可以有效地改善性能, 但是改善的程度仍然不尽人意。

还是利用硬盘擅长读写大块数据这一特性, 我们可以自己建立缓冲, 以避免上述额外的系统开销。这需要使用Input Stream和Output Stream提供的两个重载方法, 它们允许按字节读写, 也可以按字节数组读写, 如下所示:

程序清单三创建了自己的缓冲区, 即字节数组byte[]buffer, 其大小是整个文件的字节长度, 然后将整个文件一次性读入内存, 再一次性写到另一个文件。这个代码非常快, 拷贝一个8M的文本文件所花费时间降低到750ms。

注意, 使用这个策略需要权衡两个因素。首先是缓冲区的大小, 它所创建的缓冲区的大小等于被拷贝的文件大小, 当文件很大时, 该缓冲区也会很大。第二, 它为每次文件拷贝操作都要创建一个新的缓冲区, 当有大量文件需要拷贝时, JVM不得不分配和回收这些大缓冲区内存, 这对程序性能是极大的伤害。

如何在保持速度甚至速度更快的前提下避免这两个缺陷呢?方法是这样的:创建一个静态的固定长度的字节数组, 如1024*1024字节即1M, 每次只读写1M的数据。虽然对于大于1M的文件会需要多次读写才能完成整个文件的拷贝, 但是这样避免了内存的反复分配和回收。缓冲区大小是可以调整的, 针对某个具体的应用场景, 我们可以在速度和内存之间取得一个最佳的平衡。

程序清单四的copy方法使用了这样的1M字节的数组, 这个版本表现更为出色, 它拷贝一个8M的文本文件所花费时间只有218ms, 性能提高了168倍。

值得注意的是代码中的同步块, 在单线程环境下没有同步块是可以的, 但是在多线程环境下需要同步块防止多个线程同时对缓冲区实施写操作。尽管同步会带来系统开销, 但是由于while循环的次数很小 (8M文件只要循环8次) , 所以由此带来的性能损失是可以忽略不计的。实验证明, 有同步块的版本和去掉同步块的版本, 性能是一样的。

下表显示了采取四种不同的策略拷贝一个8M文件所花费的时间。它充分说明, 有效地改进缓冲机制可以大大提高程序的I/O性能。

4 结束语

一般情况下, 我们总可以为具体的应用程序找到改善I/O性能的方法, 这需要具体分析该应用程序的目的和操作特性。例如, 考虑FTP和Http服务器, 这些服务器的主要工作就是将文件从磁盘拷贝到网络的Socket, 一个网站的主页通常比其他网页访问得更多。为了提高性能, 我们可以建立快速缓冲贮存区, 将那些经常被访问的文件做缓存, 这样这些文件就不必每次从磁盘读写, 而是直接从内存拷贝到网络。

参考文献

[1]What is Java I/O?[EB/OL], http://www.roseindia.net/java/example/java/io/Java_io.shtml

[2]Buffered Input Stream, JavaTM Platform Standard Ed.7[EB/OL], http://docs.oracle.com/javase/7/docs/api/java/io/Buffered Input Stream.html

上一篇:多应用智能卡下一篇:直播栽培