1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > IO系列3-详解IO多路复用(select poll epoll)

IO系列3-详解IO多路复用(select poll epoll)

时间:2019-11-03 12:58:43

相关推荐

IO系列3-详解IO多路复用(select poll epoll)

1.重要概念

1.1 IO多路复用

I/O多路复用在英文中其实叫 I/O multiplexing。就是我们说的select,poll,epoll,有些地方也称这种IO方式为event driven IO事件驱动IO。就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。可以基于一个阻塞对象,同时在多个描述符上等待就绪,而不是使用多个线程(每个文件描述符一个线程,每次new一个线程),这样可以大大节省系统资源。所以,I/O 多路复用的特点是 通过一种机制一个进程能同时等待多个文件描述符而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回。

I/O multiplexing 这里面的 multiplexing 指的其实是在单个线程通过记录跟踪每一个Sock(I/O流)的状态(对应空管塔里面的Fight progress strip槽)来同时管理多个I/O流. 发明它的原因,是尽量多的提高服务器的吞吐能力。

是不是听起来好拗口,看个图就懂了

大家都用过nginx, nginx使用epoll接收请求,ngnix会有很多链接进来, epoll会把他们都监视起来,然后像拨开关一样,谁有数据就拨向谁,然后调用相应的代码处理。redis类似同理。

1.2 文件描述符

文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。如下:

/** @author Pavani Diwanji* @seejava.io.FileInputStream* @seejava.io.FileOutputStream* @since JDK1.0*/public final class FileDescriptor {private int fd;private Closeable parent;private List<Closeable> otherParents;private boolean closed;}复制代码

2. Linux实现IO多路复用函数

所谓 I/O 多路复用机制指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程,就是说通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作。这种机制的使用需要 select 、 poll 、 epoll 来配合。

多个连接共用一个阻塞对象,应用程序只需要在一个阻塞对象上等待,无需阻塞等待所有连接。当某条连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理。

2.1 select函数

select是第一个实现IO多路复用 (1983 左右在BSD里面实现)。select是三者当中最底层的,它的事件的轮训机制是基于比特位的。每次查询都要遍历整个事件列表。

2.1.1 数据结构和参数定义

理解select,首先要理解select要处理的fd_set数据结构,每个select都要处理一个fd_set结构。fd_set数据结构如下:

typedef long int __fd_mask;/* fd_set for select and pselect. */typedef struct{#ifdef __USE_XOPEN__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];# define __FDS_BITS(set) ((set)->fds_bits)#else__fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];# define __FDS_BITS(set) ((set)->__fds_bits)#endif} fd_set;复制代码

fd_set简单地理解为一个长度是1024的比特位,每个比特位表示一个需要处理的FD(File descriptor),如果是1,那么表示这个FD有需要处理的I/O事件,否则没有。Linux为了简化位操作,定义了一组宏函数来处理这个比特位数组。

void FD_CLR(int fd, fd_set *set);// 清空fd在fd_set上的映射,说明select不在处理该fdint FD_ISSET(int fd, fd_set *set); // 查询fd指示的fd_set是否是有事件请求void FD_SET(int fd, fd_set *set);// 把fd指示的fd_set置1void FD_ZERO(fd_set *set); // 清空整个fd_set,一般用于初始化复制代码

从上述可以看出,select能处理fd最大的数量是1024,这是由fd_set的容量决定的。

再看select的调用方式:

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);复制代码

nfds:表示表示文件描述符最大的数目+1,这个数目是指读事件和写事件中数目最大的,+1是为了全面检查readfds:表示需要监视的会发生读事件的fd,没有设置为NULLwritefds:表示需要监视的会发生写事件的fd,没有设置为NULLexceptfds:表示异常处理的,暂时没用到。。。timeout:表示阻塞的时间,如果是0表示非阻塞模型,NULL表示永远阻塞,直到有数据来timeval定义如下:

struct timeval {long tv_sec; /* seconds */

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。