码迷,mamicode.com
首页 > 其他好文 > 详细

多路复用I/O分析

时间:2015-08-27 23:00:28      阅读:169      评论:0      收藏:0      [点我收藏+]

标签:

今天看到一个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

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

多路复用I/O分析

标签:

原文地址:http://blog.csdn.net/xygl2009/article/details/48033055

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!