epoll

基本概念:
  • I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
  • epoll是在Linux 2.6内核中提出的,是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关心的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。
  • 与select,poll的对比??
接口
#include 
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

(1) int epoll_create(int size);
  创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。

(2)int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
  epoll的事件注册函数,它在这里注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:

struct epoll_event {
  __uint32_t events;  /* Epoll events */
  epoll_data_t data;  /* User data variable */
};
typedef union epoll_data {
    void *ptr;
    int fd;
    __uint32_t u32;
    __uint64_t u64;
} epoll_data_t;

events可以是以下几个宏的集合:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相`对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里.
(3)int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
  等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。

3、工作模式

epoll对文件描述符的操作有两种模式:LT(level trigger)和ET(edge trigger)。LT模式是默认模式,LT模式与ET模式的区别如下:
  LT模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程序并通知此事件。
  ET模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。
  ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

  • 二者的主要差异在于level-trigger模式下只要某个socket处于readable/writable状态,无论什么时候进行epoll_wait都会返回该socket;而edge-trigger模式下只有某个socket从unreadable变为readable或从unwritable变为writable时,epoll_wait才会返回该socket。

4、LT 水平触发(高电平)

  • 内核中的socket接收缓冲区为空 低电平
  • 内核中的socket接收缓冲区不为空 高电平
  • 内核中的发送缓冲区为满 低电平
  • 内核中的输入缓冲区不为空 高电平

EPOLLIN 事件被触发说明关注的已连接socket内核缓冲区有数据可读 此时不能马上关注EPOLLOUT。如果这个时候关注EPOLLOUT而内核缓冲区中没有数据,EPOLLOUT状态就会一直可写, 会一直触发EPOLLOUT,导致busy_loop事件
应用层的数据发送完了之后,需要取消关注EPOLLOUT事件,否则依然会引起busy_loop现象。

read(connfd,buf,sizeof(buf))...
ret = write(connfd, buf, sizeof(buf))
 ####不能在此处注册EPOLLOUT事件,因为如果写缓冲区为空,则该时间始终为高电平状态,会不断触发EPOLLOUT事件,导致budy_loop.如果write时候内核写缓冲区有数据,就会导致写操作不能写入到内核缓冲区,应该在此种情况下注册EPOLLOUT事件。
if (ret< sizeof(buf)){ //表示没有发送完
    注册EPOLLOUT事件,发送数据.此时注册事件,则会一只发送数据直到发送完,发送完应用层的数据后应该马上取消关注的EPOLLOUT事件。
}

4、异常处理

  • EMFILE 文件描述符过多

参考链接:
IO多路复用之epoll总结
IO - 同步,异步,阻塞,非阻塞 (亡羊补牢篇)
Epoll在LT和ET模式下的读写方式
Linux下的I/O复用与epoll详解
实现了一个比nginx速度更快的HTTP服务器
我的网络开发之旅——socket编程
网络编程释疑之:单台服务器上的并发TCP连接数可以有多少
彻底学会使用epoll(一)——ET模式实现分析
epoll源码分析(全)
poll&&epoll实现分析(二)——epoll实现
并发服务器--02(基于I/O复用——运用epoll技术)
高性能IO模型浅析
大并发 02 select/poll/epoll 工作过程 busy_loop的产生

你可能感兴趣的:(epoll)