信息免费建站网站有哪些,焦作网站seo,广西住房与城乡建设厅网站电话,大型网站的设计IO多路复用有几种实现方式#xff1a;select poll和epoll。本篇文章对epoll进行总结理解。
IO多路复用的含义#xff0c;我个人的理解是通过一个线程实现对多个socket的侦听#xff0c;epoll与select和poll的区别是epoll效率最高。select的最高管理1024个socket并且是通过轮…IO多路复用有几种实现方式select poll和epoll。本篇文章对epoll进行总结理解。
IO多路复用的含义我个人的理解是通过一个线程实现对多个socket的侦听epoll与select和poll的区别是epoll效率最高。select的最高管理1024个socket并且是通过轮询的方式实现的管理管理的socket个数越多耗时越长。而epoll则没有1024这个限制并且不是通过轮询的方式实现这也是epoll应用于高并发的场景的原因所在。
epoll是一种IO事件通知机制。
IO多路复用不同实现方式对比 selectpollepoll性能随着连接数的增加性能急剧下降处理成千上万的并发连接数时性能很差随着连接数的增加性能急剧下降处理成千上万的并发连接数时性能很差随着连接数的增加性能基本没有变化连接数一般1024无限制无限制内存拷贝每次调用select拷贝每次调用poll拷贝fd首次调用epoll_ctl拷贝每次调用epoll_wait不拷贝数据结构bitmap数组红黑树内在处理机制线性轮询线性轮询FD挂在红黑树通过事件回调callback时间复杂度O(n)O(n)O(log(n))
epoll是IO多路复用的一种实现方式也是目前主流的高并发实现方案。
epoll的作用
经常看到epoll的作用也知道他是IO多路复用的一种实现形式但是由于过往经历使用select比较多对epoll总是知其然而不知其所以然。
epoll主要用于对socket进行侦听实现一个线程对多个socket的管理相对于select和poll能够有效的减少系统开销性能稳定。
epoll的API接口
int epoll_create(int size);
功能该函数生成一个 epoll 专用的文件描述符。
参数size: 用来告诉内核这个监听的数目一共有多大参数 size 并不是限制了 epoll 所能监听的描述符最大个数只是对内核初始分配内部数据结构的一个建议。自从 linux 2.6.8 之后size 参数是被忽略的也就是说可以填只有大于 0 的任意值。返回值如果成功返回poll 专用的文件描述符否者失败返回-1。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能epoll 的事件注册函数它不同于 select() 是在监听事件时告诉内核要监听什么类型的事件而是在这里先注册要监听的事件类型。参数epfd: epoll 专用的文件描述符epoll_create()的返回值参数op: 表示动作用三个宏来表示
EPOLL_CTL_ADD注册新的 fd 到 epfd 中
EPOLL_CTL_MOD修改已经注册的fd的监听事件
EPOLL_CTL_DEL从 epfd 中删除一个 fd
参数fd: 需要监听的文件描述符参数event: 告诉内核要监听什么事件struct epoll_event 结构如:events 可以是以下几个宏的集合
EPOLLIN 表示对应的文件描述符可以读包括对端 SOCKET 正常关闭
EPOLLOUT表示对应的文件描述符可以写
EPOLLPRI表示对应的文件描述符有紧急的数据可读这里应该表示有带外数据到来
EPOLLERR表示对应的文件描述符发生错误
EPOLLHUP表示对应的文件描述符被挂断
EPOLLET 将 EPOLL 设为边缘触发(Edge Trigger)模式这是相对于水平触发(Level Trigger)来说的。
EPOLLONESHOT只监听一次事件当监听完这次事件之后如果还需要继续监听这个 socket 的话需要再次把这个 socket 加入到 EPOLL 队列里返回值0表示成功-1表示失败。
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
功能等待事件的产生收集在 epoll 监控的事件中已经发送的事件类似于 select() 调用。参数epfd: epoll 专用的文件描述符epoll_create()的返回值参数events: 分配好的 epoll_event 结构体数组epoll 将会把发生的事件赋值到events 数组中events 不可以是空指针内核只负责把数据复制到这个 events 数组中不会去帮助我们在用户态中分配内存。参数maxevents: maxevents 告之内核这个 events 有多少个 。参数timeout: 超时时间单位为毫秒为 -1 时函数为阻塞。返回值如果成功表示返回需要处理的事件数目
如果返回0表示已超时
如果返回-1表示失败
epoll为什么高效
说到epoll为什么高效还是要从IO多路复现的实现历史说起IO多路复用的实现最初是select然后select有几个问题
默认的select实现管理的socket数量一般为1024数量存在限制虽然可以修改但是需要重新编译内核每次调用select接口都会将侦听的fd的数组从用户态内存拷贝到内核态缓冲区另外当有socket可读或者可写时也会将fd数组从内核态缓冲区拷贝至用户态内存。用户态至内核态或者内核态至用户态数据的拷贝这样的拷贝对于资源的消耗是很大的。无论是内核态还是用户态由于保存fd的是一个数组都需要通过轮询的方式遍历fd数组找到可读或者可写的fd当fd数量增大时性能是下降的。
select运行原理示意视频
select-CSDN直播
针对select存在这样的问题后续发展出了poll但是poll相对于select的优化有限仅仅只改善了select管理socket上线的问题其余两点都没有进行优化。
再往后就发展了出了epollepoll相对于select和poll出现了跨越式的改进将select涉及的问题都做了响应的改进
管理的socket无上限而且是通过函数传参的形式指定管理的socket个数而select是通过头文件中的FD_SIZE来指定的。不言而喻通过函数传参的方式更灵活。epoll内部管理fd的数据结构是红黑树查找、修改和删除的时间复杂度都很优秀。epoll_wait的每次调用不会向select调用一样每次都会产生用户态到内核态的拷贝从而减少资源消耗当内核检测到某个fd的可读或者可写事件时会自动调用该fd的poll回调函数将该fd的信息拷贝到数组中epoll仅会将检测到可读可写的事件fd写入到数组中传递到用户态内存中这一点与select是不同的select是要所有监听的fd的集合拷贝到用户区中。
总结起来就是
管理的socket无上限用户态内存和内核缓冲区内存拷贝次数减少传递出的可读或者可写的事件仅包含这些可读可写的fd这一点也是与select不同的select传出的是所有fd的集合。
epoll运行原理示意视频
epoll-CSDN直播
epoll的触发方式
epoll有两种触发方式一种是水平触发一种是边缘触发。
水平触发这种触发方式的含义是只要读缓冲区存在数据epoll会一直提示该fd有可读事件当为写缓冲区时如果写缓冲区空间不满则epoll_wait会提示用户该fd有可写事件。epoll默认的触发方式是水平触发。 对于读操作只要缓冲内容不为空LT模式返回读就绪。 对于写操作只要缓冲区还不满LT模式会返回写就绪。 当被监控的文件描述符上有可读写事件发生时epoll_wait()会通知处理程序去读写。如果这次没有把数据一次性全部读写完(如读写缓冲区太小)那么下次调用 epoll_wait()时它还会通知你在尚没读写完的文件描述符上继续读写当然如果你一直不去读写它会一直通知你。如果系统中有大量你不需要读写的就绪文件描述符而它们每次都会返回这样会大大降低处理程序检索自己关心的就绪文件描述符的效率。 边缘触发只有当缓冲区的状态发生变化的时候才会触发可读可写事件。例如读缓冲区内由无数据变为有数据只有此种情况下才会触发可读事件也就是说对于读缓冲区读缓冲区从数据变为有数据只会发送一次可读事件至于读缓冲区内的事件是否读完不太关心需要用户自己去处理若为写缓冲区写缓冲区由不可写入变为可以写入的情况下会触发可写事件其余情况不会触发该事件。若要修改边沿触发模式则需要调用epoll_ctl接口修改在event参数中添加EPOLLET即可。 对于读操作 当缓冲区由不可读变为可读的时候即缓冲区由空变为不空的时候。 当有新数据到达时即缓冲区中的待读数据变多的时候。 当缓冲区有数据可读且应用进程对相应的描述符进行EPOLL_CTL_MOD 修改EPOLLIN事件时。 对于写操作 当缓冲区由不可写变为可写时。 当有旧数据被发送走即缓冲区中的内容变少的时候。 当缓冲区有空间可写且应用进程对相应的描述符进行EPOLL_CTL_MOD 修改EPOLLOUT事件时。 当被监控的文件描述符上有可读写事件发生时epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小)那么下次调用epoll_wait()时它不会通知你也就是它只会通知你一次直到该文件描述符上出现第二次可读写事件才会通知你。这种模式比水平触发效率高系统不会充斥大量你不关心的就绪文件描述符。 在ET模式下 缓冲区从不可读变成可读会唤醒应用进程缓冲区数据变少的情况则不会再唤醒应用进程。 对于水平触发和边缘触发更形象的解释 水平触发0为无数据1为有数据。缓冲区有数据则一直为1则一直触发。 边缘触发发0为无数据1为有数据只要在0变到1的上升沿才触发。 JDK并没有实现边缘触发Netty重新实现了epoll机制采用边缘触发方式另外像Nginx也采用边缘触发。 JDK在Linux已经默认使用epoll方式但是JDK的epoll采用的是水平触发而Netty重新实现了epoll机制采用边缘触发方式netty epoll transport 暴露了更多的nio没有的配置参数如 TCP_CORK, SO_REUSEADDR等等另外像Nginx也采用边缘触发。 epoll与设计模式的关系
待补充 参考链接
epoll详解
不同的IO多路复用具体实现