有关linux多路复用器(select,poll,epoll)的一些总结

select:

Select的官方翻译为同步多路复用器(synchronous I/O multiplexing),函数描述为:

int select(int nfds, fd_set *readfds, fd_set *writefds,

                  fd_set *exceptfds, struct timeval *timeout);

具体用法可见官方文档。

Select函数可以使程序员监控多个文件描述符,直到一个或多个文件描述符对标准I/O变为就绪状态(文件描述符准备好处理I/O操作时,被任务是处于就绪状态,read或write可以执行而不会阻塞)。

Select有三个fd_set型的参数,可以认为是三个文件描述符集合,分别被用来监控读就绪,写就绪和异常事件,每个参数都可以为空,在函数返回的时候,这三个文件描述符集合将会被修改为指示当前已处于就绪状态的文件描述符集合,所以当select()函数在一个循环里调用的时候,需要在每次调用前都对参数进行重新初始化。

Select函数返回一个整数(0和-1表示timeout时间内没就绪事件和发生错误)表示就绪的文件描述符数量,所以在函数返回以后还需要调用read或write函数进行读写操作,并且这个过程也是同步的。

 

Poll

Poll函数和select函数非常类似(synchronous I/O multiplexing),poll函数主要是为了解决select函数的文件描述符集合类型(fd_set)不能指定大于1023个文件描述符的缺陷,poll函数使用了一个新的类型pollfd:

struct pollfd {
               int   fd;         /* file descriptor */
               short events;     /* requested events */
               short revents;    /* returned events */
           };

以及参数nfds类指定关心的fd、事件和fd数量(fds参数为pollfd指针,不能直接获取到数量)。Poll的返回值也和select函数类型,返回就绪的文件描述符数量,所以后续也需要进行read或write调用进行fd的读写。

epoll

Epoll表示I/O事件通知机制(I/O event notification facility),他也能监控多个文件描述符的I/O事件状态,同时epoll API还能够选择电平触发或者沿触发,epoll API的主要思想是在内核空间epoll instance数据结构,这个数据结构总体上可以看做是包含两个列表的容器:一个是interest list(或者成为epoll set),这个list用来记录进程感兴趣的文件描述符;还有一个是ready list,用户记录I/O处于“ready”状态的文件描述符,ready list 是 interest list 的子集,ready list是内核根据I/O的结果动态改变的,Epoll事件通知机制提供了三个API:

epoll_create:用来创建一个新的epoll instance 并返回一个文件描述符与这个epoll instance相关联:

 #include 

       int epoll_create(int size);

epoll_ctl:用来注册进程关心的文件描述符事件到对应epoll instance的 interest list中去;

       #include 

       int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

epoll_wait:等待I/O事件,如果当前没有I/O事件时,调用会阻塞线程,可以设置最长阻塞时间(这个接口有两个非常典型的应用:redis和nginx。这两个非常常见的中间件底层都是通过epoll的系统调用来实现高性能的IO多路复用,不同的是:①redis由于单线模型,所以阻塞时间设置为0,相当于非阻塞调用,故在没有I/O事件发生的时候,主线程会一直在这个调用上循环,这样不会影响主线程的其他任务;②nginx为bossThread和workThread的多线程模型,bossThread在内部只用来监听socket事件,简历链接,所以bossThread在调用epoll_wait时设置的阻塞时间为-1,意思是无限期阻塞直到I/O事件发生)

       #include 

       int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);

java的NIO在linux上也是基于epoll的机制实现的,其中最重要的三个组件:Selector,Channel,Buffer在加上一个SelectionKey,也能在epoll的实现思想上找到对应的影子(天下代码一大抄,这也是写代码正确的姿势)。

最后需要注意的一点是:epoll_wait函数的返回值是个int类型,代表有多少个fd的I/O处于ready状态,然后程序再通过ready list获取到具体的fd信息,再进行读写,然而读写操作还是需要程序自己去执行read或write的系统调用,然而这个过程还是同步的,只是说在获取fd的I/O状态上是非阻塞的,而真正执行I/O是同步的,这点和select相同,所以现在绝大多数的高性能应用都是同步非阻塞的I/O模型(目前在linux上的异步读写还不是很稳定,市面上也没有成熟的产品)。

最最后:eopll没有完全取代select的原因是在连接数较少,且连接比较活跃的情况下,select的调用更加方便,且性能也可能比epoll更好,所以,select还是有其适用的场景的。

你可能感兴趣的:(JDK源码)