多路复用I/O分析

今天看到一个I/O性能的问题。就对这个问题思考了下。

分析阻塞、非阻塞I/O,这两种I/O一个共同点是,很多I/O中无法确认那些I/O是准备好,只能通过一个个轮询的方式,这种方式下,准备好与没有准备好的I/O均会被轮询,这种效率极其低下。异步I/O,提供了一种方式,准备好的I/O,就会发送一个信号给内核,其他时间继续进行其他的操作,这种方式一个不好的就是多个I/O同时准备好,发出的信号不好处理,不知道那个信号对应于那个描述符。

这样就思考,能不能有一种I/O呢。只考虑准备好的I/O,没准备好的I/O,不进行操作。

其实是有的,这种I/O就是多路复用I/O。在处理I/O之前,我们需要调用函数进行处理所监控的I/O,判断读写事件,等待其就绪后进行操作。这些函数有select、pselect、poll、epoll(Linux2.6内核后支持)。

首先我们来看下select函数,分别需要看传递的参数与返回的结果。

传递给select的函数,主要告诉内核,我们感兴趣的描述符,对于每个描述符,我们所关心的状态,监控这些I/O需要等待多久。

select函数返回的时候,我们可以知道已经准备好的描述符的数量;对于读、写、异常这三个状态的每一个,那些描述符已经具备了。

select函数的原型如下:

#include<sys/select.h>
int select(int maxfdp1,fd_set *restirct readfds,
fd_set *restrict writefds,fd_set *restrict exceptfds,
struct timeval *resttrict tvptr );
这里需要说明下这个等待时间tvptr。上面参数中每个指针都添加了关键字restrict,限定只有声明的该指针才能访问这块内存,其他指针访问都是无效的。

当tvptr==NULL,select函数会永远等待,直到捕捉到监控的描述符中有准备好的或者一个出错信号,若出错则返回-1,否则返回准备就绪的描述符的数量;

当tvptr->tv_sec==0 && tvptr->tv_usec==0,select函数不等待,测试所有指定的描述符立刻返回,得到每个描述符的状态,非阻塞I/O轮询每个描述符;

当tvptr->tv_sec!=0 || tvptr->tv_usec!=0,select函数会等待指定的时间,超时返回0。如果在等待的时间内有准备好的句柄,则返回准备好的句柄的数量。在等待的时间内,select函数可以被信号打断。

第一个参数maxfdp1,这个参数的意思是最大的描述符加1,这样的话就需要在三个集合中找出最大描述符,然后加一传递给这个变量。其实这个值可以传递一个默认值,FD_SETSIZE,这是内核维护的一个常量,其值一般为1024,表示select函数最多可以处理的描述符的个数为1024个。若想要让内核支持多一些,可以修改此参数。

然后,我们继续看函数,里面有三个参数需要注意readfds、writefds、exceptfds,这三个分别表示处于可读、可写、异常的句柄的集合,这些句柄都存储在一个叫做fd_set数据类型里。现在看下关于这个集合的四种基本的操作:

#include<sys/select.h>

int  FD_ISSET(int fd,fd_set *fdset);
void FD_SET(int fd,fd_set *fdset);
void FD_CLR(int fd,fd_set *fdset);
void FD_ZERO(fd_set *fdset);

首先看第一个函数,FD_ISSET该函数用于测试句柄fd是否在fdset内,如果再返回一个非零值,如果不在则返回0。FD_SET将句柄fd添加到集合fdset中,FD_CLR从集合fdset中清除句柄fd,FD_ZERO,初始化集合fdset,使得每个位置都为0.再以往select函数中添加参数为例,详细了解下操作的过程,可以看下面的代码:

fd_set  readset,writeset;
FD_ZERO(&readset);
FD_ZERO(&writeset);
FD_SET(0,&readset);
FD_SET(3,&readset);
FD_SET(2,&writeset);
FD_SET(4,&writeset);
FD_SET(1,&writeset);
select(5,&readset,&writeset,NULL,NULL);

下面说下pselect,这是select的一个升级版本,提供更加精细的定时操作与信号屏蔽字,函数原型如下:

int pselect(int maxfdp1,fd_set *restirct readfds,
fd_set *restrict writefds,fd_set *restrict exceptfds,
const struct timespec *resttrict tsptr,const sigset_t *restrict sigmask);

这个函数与select不同的一点是,首先是定时器方面由timeval换成了timespec,这个更加精细,可以精确到微秒级别。还有一个信号屏蔽字sigmask。可以以原子的方式安装信号屏蔽字,函数返回时恢复以前的信号屏蔽字。关于怎么设置信号屏蔽字,可以看看这篇博客

上面所有的就是对select函数的全部理解。

下面看下另外一个函数,叫做poll。

poll函数起源于system v,poll函数与STREAM系统紧紧相关。其函数原型如下:

#include<poll.h>
int poll(struct pollfd fdarray[],nfds_t nfds,int timeout);

poll与select不同的一点,select将描述符按照状态编入一个集合中,poll形成一个pollfd结构数组,数组内每个元素指定一个描述符编号以及对其所关心的状态。

struct pollfd{
     int fd;
     short events;
     short revents;
};

fdarray的元素个数由nfds决定。结构体中的events告诉内核对该描述符应当关心的是什么,revents是内核操作完后返回值,用以说明内核对该描述符进行了什么操作。

当一个描述符被挂断掉后,不能向描述符写数据,但是可以读取文件描述符的数据。

poll函数的最后一个timeout,当为-1时候,永远等待,直到捕捉到信号,poll返回-1。如果所指定的描述符准备好了,则返回准备好的文件描述符的个数。当为0的时候,不等待,测试所有描述符并返回,这里会以非阻塞轮询的方式处理所有描述符。当timeout大于0,就等待timeout毫秒,超时返回0,否则返回准备好的个数。

最后一种epoll方式,可以看看本博客前面的总结:epoll

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-29 19:06:23

多路复用I/O分析的相关文章

libevent 网络IO分析

libevent 网络IO分析 Table of Contents 1. 简介 2. 简单使用与入门 2.1. 定时器-timeout 超时回调 2.2. 信号事件 2.3. 读取 socket 3. 操作系统 I/O 模型封装 4. 源码分析-基本功能 4.1. 超时机制-对 timeout 例子进行源码分析 4.1.1. libevent 初始化 4.1.2. 注册 event 4.1.3. dispatch 进入循环,分发回调 4.1.4. 兼备事件优先级,统一处理回调 4.2. I/O

Socket编程模式

Socket编程模式 本文主要分析了几种Socket编程的模式.主要包括基本的阻塞Socket.非阻塞Socket.I/O多路复用.其中,阻塞和非阻塞是相对于套接字来说的,而其他的模式本质上来说是基于Socket的并发模式.I/O多路复用又主要分析了分析linux和windows下的常用模型.最后,比较这几种Socket编程模式的优缺点,并讨论多线程与Socket的组合使用和服务器开发的常用模式. 阻塞模式 阻塞模式是最基本的Socket编程模式,在各种关于网络编程的书籍中都是入门的例子.就像其

Socket编程模式理解与对比

本文主要分析了几种Socket编程的模式.主要包括基本的阻塞Socket.非阻塞Socket.I/O多路复用.其中,阻塞和非阻塞是相对于套接字来说的,而其他的模式本质上来说是基于Socket的并发模式.I/O多路复用又主要分析了分析linux和windows下的常用模型.最后,比较这几种Socket编程模式的优缺点,并讨论多线程与Socket的组合使用和服务器开发的常用模式. 阻塞模式 阻塞模式是最基本的Socket编程模式,在各种关于网络编程的书籍中都是入门的例子.就像其名所说,阻塞模式的So

IO多路复用的几种实现机制的分析

http://blog.csdn.net/zhang_shuai_2011/article/details/7675797 select,poll,epoll都是IO多路复用的机制.所谓I/O多路复用机制,就是说通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作.但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步

{python之IO多路复用} IO模型介绍 阻塞IO(blocking IO) 非阻塞IO(non-blocking IO) 多路复用IO(IO multiplexing) 异步IO(Asynchronous I/O) IO模型比较分析 selectors模块

阅读目录 一 IO模型介绍 二 阻塞IO(blocking IO) 三 非阻塞IO(non-blocking IO) 四 多路复用IO(IO multiplexing) 五 异步IO(Asynchronous I/O) 六 IO模型比较分析 七 selectors模块 一 IO模型介绍 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?这个问题其实不同的人给出的答案都可能

自动化运维Python系列之IO多路复用、SocketServer源码分析

IO多路复用 IO多路复用是指:通过一种机制,可以监视多个描述符,一旦某个系统描述符就绪(一般是读就绪或者写就绪)能够通知程序进行相应的读写操作 实例化例子就是在SocketServer模块中,客户端和服务端建立好连接,此时服务端通过监听conn这条链路,一旦客户端发送了数据,conn链路状态就发生变化,服务端就知道有数据要接收... Linux系统中同时存在select.pull.epoll三种IO多路复用机制 windows中只有select机制 1)select select本质上是通过设

I/O多路复用之epoll

1.select.poll的些许缺点 先回忆下select和poll的接口 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); int poll(struct pollfd *fds, nfds_t nfds, int timeout); 这两个多路复用实现的特点是: 每次调用select和poll都要把用户关心的事件集合(select为readf

I/O多路复用之select

1.什么是I/O多路复用 关于什么是I/O多路复用,在知乎上有个很好的回答,可以参考罗志宇前辈的回答. 这里记录一下自己的理解.我认为要理解这个术语得从两方面去出发,一是:多路是个什么概念?二是:复用的什么东西?先说第一个问题.多路指的是多条独立的i/o流,i/o流可以这么理解:读是一条流(称之为读流,比如输入流),写是一条流(称之为写流,比如输出流),异常也是一条流(称之为异常流),每条流用一个文件描述符来表示,同一个文件描述符可以同时表示读流和写流.再来看第二个方面,复用的是什么东西?复用的

TCP协议可靠性数据传输实现原理分析

http://blog.csdn.net/chexlong/article/details/6123087 TCP 协议是一种面向连接的,为不同主机进程间提供可靠数据传输的协议.TCP 协议假定其所使用的网络栈下层协议(如IP 协议)是非可靠的,其自身提供机制保证数据的可靠性传输.在目前的网络栈协议族中,在需要提供可靠性数据传输的应用中,TCP 协议是首选的,有时也是唯一的选择.TCP 协议是在最早由Cerf 和Kahn[1]所提出的有关网络数据包传输协议的概念之上建立的.TCP 协议被设计成符