后台开发,网络交互是必须的,而epoll基本绝大多数网络框架的必备武器,本文对epoll进行详细的介绍,包括epoll的作用,优点,接口,实现原理等。

一. epoll是什么

  epoll是一种IO多路转接技术,在LINUX网络编程中,经常用来做事件触发,即当有特定事件到来时,能够检测到,而不必阻塞进行监听。

  epoll有两种工作方式,ET-水平触发 和 LT-边缘触发(默认工作方式),主要的区别是:

  LT,内核通知你fd是否就绪,如果没有处理,则会持续通知。而ET,内核只通知一次。

二. epoll的优点

  与select相比,epoll有以下优点:

  2.1. 支持进程打开大量数目的socket描述符,select支持的进程描述符由FD_SETSIZE设置,默认值为

     1024,而epoll不受这个限制。

  2.2. epoll的效率,不随监听的socket数目增加而线性下降。

     select采用轮询的方式,对socket集合的描述符表进行扫描,如果socket数量过大,并且大多数

     socket属于idle状态,select的扫描就做了很多无用功。

     epoll只会对活跃的socket进行操作,所以,在socket数量比较大,而绝大多数socket属于idle

     状态时,epoll的效率会远胜于select。如果绝大多数socket是活跃的,由于epoll_ctl的影响,

     epoll的效率会稍微比select差。

  3.3. 使用mmap加速内核与用户空间的传递,关于mmap,找个时间做个详细的介绍。

三. epoll的接口

  epoll主要有三个接口

  3.1 int epoll_create( int size )

    创建一个epoll的句柄,size表示监听的数目一共有多大,我们现网的服务器,每个进程是设置    

    12W。

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

    事件注册函数,epfd是epoll_create返回的句柄,op是表示做什么动作,用三个宏表示:

        EPOLL_CTL_ADD:注册新的fd到epfd中;
        EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
        EPOLL_CTL_DEL:从epfd中删除一个fd;

        fd表示要监听的描述符,event表示内核要监听什么事,由以下几个宏表示:

        EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
        EPOLLOUT:表示对应的文件描述符可以写;
        EPOLLPRI:表示对应的文件描述符有紧急的数据可读;
        EPOLLERR:表示对应的文件描述符发生错误;
        EPOLLHUP:表示对应的文件描述符被挂断;
        EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
        EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把     

       这个socket加入到EPOLL队列里

  3.3 int epoll_wait( int epfd, struct epoll_event* events, int maxevents, int time_out )

    等待事件的发生。events,存储epoll_wait操作完成后,存储的事件。maxevents表示当前要监听的

    所有socket句柄数。time_out为超时时间。返回值表示需要处理的事件数目,0表示超时。


四. epoll的实现原理

     epoll_create

     在epoll文件系统建立了个file节点,并开辟epoll自己的内核高速cache区,建立红黑树,分配

     好想要的size的内存对象,建立一个list链表,用于存储准备就绪的事件。  

 

     epoll_ctl

     把要监听的socket放到对应的红黑树上,给内核中断处理程序注册一个回调函数,通知内核,如果

     这个句柄的数据到了,就把它放到就绪列表。


     epoll_wait

     观察就绪列表里面有没有数据,并进行提取和清空就绪列表,非常高效。


     LT和ET实现的区别?

     由epoll_wait进行实现,如果是LT模式,它发现soket上还有未处理的事件,则在清理就绪列表

     后,重新把句柄放回刚刚清空的就绪列表。


     如果所示,红黑树,就绪列表,小段cache,就解决了大并发的问题,果然是简单才是美。