Linux Socket 编程领域,为了处理大量连接请求场景,需要使用非阻塞IO和复用。
select , poll 是Linux API 提供的IO 复用方式。
自从Linux 2.6 加入epoll之后,高性能服务器领域得到广泛应用,Nginx 就是使用epoll 来实现IO复用支持高并发。
select模型
int poll (struct pollfd *fds, unsigined int nfds, int timeout);
不同于select 使用三个参数 表示三个fdset 的方式,poll 使用一个pollfd 的指针实现。
struct pollfd{ int fd;/* 文件描述 */ short events; /**/ short revents; /**/ }
pollfd 结构体包含了要监视的 event 和发生的 event.
pollfd 没有数量限制(但是数量过大后性能也会下降)。
和 select 函数一样,poll 返回后,需要轮询pollfd 来获取就绪的描述符。
select 和 poll 都需要在返回后,通过遍历文件描述符 来 获取已经就绪的socket.
大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述数量的增长,效率也会线性下降。
epoll模型
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);
epoll_create 函数 创建epoll 文件描述符 size 一个建议值。
epoll_ctl 完成对 描述符fd 执行 op 操作控制, event 与 fd 关联的监听事件。
op 操作有三种 添加 EPOLL_CTL_ADD,删除EPOLL_CTL_DEL,修改EPOLL_CTL_MOD。
分别添加/删除/修改 对fd 的监听事件。
epoll_wait 等待epfd 上的IO事件,最多返回maxevents个事件。
在select/poll中,进程只有在调用一定方法后,内核才对所有监视的文件描述符进行扫描,
而epoll 事先通过epoll_ctl 注册一个文件描述符,一旦某个文件描述就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait时便得到通知。
epoll 的优点
1.监视的描述符数量不受限制,1GB 内存大约时10万左右。cat /proc/sys/fs/file-max
2.IO的效率不会随着监视fd 的数量增长而下降。epoll 不同于select/poll 的轮询方式,而是通过每个fd定义的回调函数来实现。就绪的fd才会执行回调函数。
3.支持水平触发,边沿触发俩种模式。
水平触发模式:文件描述符 状态改变后,如果没有采取行动,它将后面反复通知,这种情况相对简单,libevent 等开源库采用这种模式。
边沿触发模式:只告诉进程哪些文件描述符 刚刚变为就绪态,只说一遍,如果没有采取行动,不会再次告知。性能更高,Nginx使用边沿触发。
4.mmap 加速内核与用户空间的信息传递。epoll 是通过内核与用户空间mmap 同一块内存,避免无谓的内存拷贝。