ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Epoll,Poll,Select备忘录

Epoll,Poll,Select备忘录 目录拓展理解水平触发 vs. 边缘触发Select1. 它是如何工作的索引即编号2. 它最大的优点极度节省内存3. 它最大的缺陷固定尺寸的“紧箍咒”Poll 简单总结poll 工作方式poll 的缺点Epoll 简单总结epoll 模型Epoll常见状态如下ET模式下为什么要设置非阻塞gitgithub.com:lxxlovelp/I-O.git //github 源码地址select、poll、epoll都是 Linux 下用于I/O 多路复用I/O Multiplexing的机制。它们的核心作用是让一个线程同时监控多个文件描述符file descriptor简称 fd当其中某些fd 可读、可写或发生异常时通知程序进行处理。拓展理解水平触发 vs. 边缘触发这是epoll独有的强大特性水平触发 (LT)只要文件描述符处于就绪状态如缓冲区有数据每次调用epoll_wait都会通知你。这是select和poll的唯一模式也是epoll的默认模式处理起来更安全不容易遗漏数据。边缘触发 (ET)仅在文件描述符的状态发生变化时如从不可读变为可读通知一次。这种模式更高效减少了重复通知的次数但要求开发者必须一次性把数据读完否则可能丢失通知编程更复杂。Select位图Bitmap本质上就是一个“由 0 和 1 组成的二进制数组”它的核心作用是用极其节省空间的方式来标记哪些“文件描述符fd”正在被监控。1. 它是如何工作的索引即编号在select中位图利用了一个巧妙的映射二进制位的索引第几位 文件描述符的数字fd。假设你要监控一个socket它的 fd 是5。系统只需要把位图中从右往左数的第 5 位标记为1。当内核检查时它直接看第 5 位是 0 还是 1就知道这个 fd 需不需要处理。2. 它最大的优点极度节省内存假设系统最多支持 1024 个连接FD_SETSIZE限制。如果用数组存1024 个 int 需要1024 * 4 4KB。如果用位图存只需要 1024 个 bit比特也就是128 个字节1024 / 8。因为存储空间极小它特别适合早期内存宝贵的时代。3. 它最大的缺陷固定尺寸的“紧箍咒”这也是你在表格里看到的“最大 1024 个连接”限制的根本原因。select使用的位图类型是内核写死的fd_set它的大小在编译时就固定了通常是 1024 位。致命操作当你的 fd 编号超过 1024比如你打开了第 1025 个文件位图的索引溢出了select会直接崩溃或忽略它这就是著名的FD_SETSIZE限制。Poll 简单总结poll是对select的改进主要解决了select的两个问题没有 fd 数量限制理论上select受FD_SETSIZE限制通常 1024。poll使用数组保存 fd可以监听更多连接。不用维护 max_fdselect需要select(max_fd 1, ...)poll直接传数组长度poll(fds, nfds, timeout)poll 工作方式用户维护一个数组struct pollfd fds[];例如下标: 0 1 2 fd: 5 8 10表示我要监听 fd5、8、10调用poll(fds, nfds, -1);内核检查这些 fd。返回后revents告诉你fd8 有数据然后if(fds[1].revents POLLIN) { read(8,...); }poll 的缺点虽然没有max_fd但是每次调用仍然需要用户态数组 | | 拷贝 ↓ 内核 | | 遍历所有fd ↓ 返回如果有10000 个连接但是只有10 个活跃poll 仍然需要检查10000 个fd复杂度O(n)Epoll 简单总结epoll是 Linux 下为高并发设计的 IO 多路复用机制。它解决了 poll 最大的问题不需要每次把所有 fd 重新交给内核检查。epoll 模型先注册epoll_ctl(epfd, ADD, fd)告诉内核我要监听 fd1 fd2 fd3 ...内核保存红黑树: 保存所有监听fd 就绪队列: 保存发生事件的fd之后epoll_wait()只返回真正有事件的fd例如10000个连接监听: 10000 实际活跃: 5返回fd12 fd88 fd900 fd5000 fd7000不用扫描其他9995个Epoll常见状态如下标志含义什么时候触发EPOLLIN可读事件socket 有数据可读EPOLLOUT可写事件socket 可以发送数据EPOLLRDHUP对端关闭写方向客户端关闭连接或半关闭EPOLLHUP挂起事件连接异常关闭EPOLLERR错误事件socket 出错EPOLLPRI紧急数据有带外数据OOBEPOLLET边缘触发模式状态变化时通知一次EPOLLONESHOT一次性事件触发后自动失效EPOLLEXCLUSIVE独占唤醒多线程监听同一个 epoll特性selectpollepoll监控方式主动轮询每次调用都需将所有待监控的文件描述符从用户态拷贝到内核态内核再遍历检查每个描述符的状态。主动轮询基本同select只是存储方式从位图变为数组。事件驱动通过epoll_ctl在内核中建立一张事件表当描述符就绪时内核通过回调函数将其加入就绪列表应用只需读取该列表。数据结构位图 (Bitmap)使用固定大小的fd_set位图存储。动态数组使用pollfd结构体数组无大小限制。红黑树 就绪链表红黑树高效管理大量描述符就绪链表只存活跃的连接。工作模式仅水平触发 (LT)仅水平触发 (LT)支持水平触发 (LT) 和边缘触发 (ET)ET模式下为什么要设置非阻塞“每 recv 一次缓冲区就会少点东西”每次recv()系统调用内核都会将接收缓冲区中的数据拷贝到用户态内存并移动内核内部的“读指针”缓冲区占用的空间随之释放。“直到缓冲区东西被拿完”通过while循环持续调用recv()就是在执行这个“耗尽”操作。“下次再 recv 的时候内核返回 EAGAIN”当内核检测到该 socket 的接收缓冲区已空且文件描述符被设置为非阻塞时它不会让线程休眠而是直接返回-1并将errno精准地设置为EAGAIN。阻塞模式recv的核心语义是“等”。只要缓冲区里没有数据它不会返回任何错误码而是直接将当前线程挂起休眠直到有数据到来才被唤醒。非阻塞模式recv的核心语义是“看”。缓冲区有数据就复制返回没有数据就立刻返回-1并精准附带errno EAGAIN。
返回列表