Linux IO多路复用 epoll 心得

epollLinux内核为处理大批量文件描述符而作了改进的poll, 是Linux下多路复用IO接口select/poll的增强版本, 它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候, 它无须遍历整个被侦听的描述符集, 只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。

相关文件到Linux公社资源站下载:

------------------------------------------分割线------------------------------------------

免费下载地址在

用户名与密码都是

具体下载目录在 /2017年资料/1月/31日/Linux IO多路复用 epoll 心得/

下载方法见

------------------------------------------分割线------------------------------------------

二、epoll的API函数

1. 句柄创建函数

int epoll_create(int size);

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

int epoll_create1(int flag);

这个函数是在linux 2.6.27中加入的, 其实它和epoll_create差不多, 不同的是epoll_create1函数的参数是flag。

当flag是0时, 表示和epoll_create函数完全一样, 不需要size的提示了。

当flag = EPOLL_CLOEXEC, 创建的epfd会设置FD_CLOEXEC, 它是fd的一个标识说明, 用来设置文件close-on-exec状态的。

当flag = EPOLL_NONBLOCK, 创建的epfd会设置为非阻塞。

2. 事件操作函数

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

第一个参数epfd, 为epoll_create返回的的epoll文件描述符。

第二个参数op表示操作值。有三个操作类型:

EPOLL_CTL_ADD  //注册目标fd到epfd中, 同时关联内部event到fd上

EPOLL_CTL_MOD  //修改已经注册到fd的监听事件

EPOLL_CTL_DEL  //从epfd中删除/移除已注册的fd, event可以被忽略, 也可以为NULL

第三个参数fd表示需要监听的fd。

第四个参数event表示需要监听的事件。

event参数是一个枚举的集合, 可以用“|”来增加事件类型, 枚举如下:

// EPOLLIN: 表示关联的fd可以进行读操作了。

// EPOLLOUT: 表示关联的fd可以进行写操作了。

// EPOLLRDHUP(since Linux 2.6.17): 表示套接字关闭了连接, 或者关闭了正写一半的连接。

// EPOLLPRI: 表示关联的fd有紧急优先事件可以进行读操作了。

// EPOLLERR: 表示关联的fd发生了错误, epoll_wait会一直等待这个事件, 所以一般没必要设置这个属性。

// EPOLLHUP: 表示关联的fd挂起了, epoll_wait会一直等待这个事件, 所以一般没必要设置这个属性。

// EPOLLET: 设置关联的fd为ET的工作方式, epoll的默认工作方式是LT。

// EPOLLONESHOT(since Linux 2.6.2): 设置关联的fd为one-shot的工作方式。表示只监听一次事件, 如果要再次监听, 需要把socket放入到epoll队列中。

3. 事件等待函数

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

int epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout,  const sigset_t *sigmask);

上面两个函数的参数含义:

第一个参数:表示epoll_wait等待epfd上的事件。

第二个参数:events指针携带有epoll_data_t数据。

第三个参数:maxevents告诉内核events有多大, 该值必须大于0。

第四个参数:timeout表示超时时间(单位: 毫秒), 为0的时候表示马上返回, 为-1的时候表示一直等下去, 直到有事件返回, 为任意正整数的时候表示等这么长的时间, 如果一直没有事件, 则返回。一般情况下, 如果网络主循环是单独的线程的话, 可以用-1来等, 这样可以保证一些效率, 如果是和主逻辑在同一个线程的话, 则可以用0来保证主循环的效率。

epoll_pwait(since linux 2.6.19)允许一个应用程序安全的等待, 直到fd设备准备就绪, 或者捕获到一个信号量。其中sigmask表示要捕获的信号量。

函数如果等待成功, 则返回fd的数字; 0表示等待fd超时, 其他错误号请查看errno。

4. 句柄关闭函数

int close(int fd);

返回值: 若文件顺利关闭则返回0, 发生错误时返回-1。

三、epoll的2种触发模式

1. Level Triggered (LT) 水平触发

LT是epoll默认的触发方式, 如下:

socket接收缓冲区不为空, 有数据可读, 则读事件一直触发;

socket发送缓冲区不满, 可以继续写入数据, 则写事件一直触发;

LT的处理过程:

accept一个连接, 添加到epoll中监听EPOLLIN事件;

当EPOLLIN事件到达时, read fd中的数据并处理;

当需要写入数据时, 先直接把数据write到fd中; 如果数据较大, 无法一次性写入, 那么在epoll中监听EPOLLOUT事件;

当EPOLLOUT事件到达时, 继续把数据write到fd中; 如果数据写入完毕, 那么在epoll中关闭EPOLLOUT事件;

2. Edge Triggered (ET) 边沿触发

socket的接收缓冲区状态变化时触发读事件, 即空的接收缓冲区刚接收到数据时触发读事件;

socket的发送缓冲区状态变化时触发写事件, 即满的缓冲区刚空出空间时触发读事件;

仅在状态变化时触发事件

ET的处理过程:

accept一个连接, 添加到epoll中监听EPOLLIN|EPOLLOUT事件;

当EPOLLIN事件到达时, read fd中的数据并处理, read需要一直读, 直到返回EAGAIN为止;

当需要写出数据时, 把数据write到fd中, 直到数据全部写完, 或者write返回EAGAIN;

当EPOLLOUT事件到达时, 继续把数据write到fd中, 直到数据全部写完, 或者write返回EAGAIN;

ET模式下, 正确的accept要考虑2个问题:

(1) 阻塞模式下, accept存在的问题

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/797e42ca55d3ef845caadc614dc7eaef.html