Skip to main content

Posts

Showing posts from December 18, 2022

深入学习IO多路复用 select/poll/epoll 实现原理

  select/poll/epoll 是 Linux 服务器提供的三种处理高并发网络请求的 IO 多路复用技术,是个老生常谈又不容易弄清楚其底层原理的知识点,本文打算深入学习下其实现机制。 Linux 服务器处理网络请求有三种机制,select、poll、epoll,本文打算深入学习下其实现原理。 吃水不忘挖井人,最近两周花了些时间学习了张彦飞大佬的文章 图解 | 深入揭秘 epoll 是如何实现 IO 多路复用的 和 其他文章 ,及出版的书籍《深入理解 Linux 网络》,对阻塞 IO、多路复用、epoll 等的实现原理有了一定的了解;飞哥的文章描述底层源码逻辑比较清晰,就是有时候归纳总结事情本质的抽象程度不够,涉及内核源码细节的讲述较多,会让读者产生一定的学习成本,本文希望在这方面改进一下。 0. 结论 本文其他的内容主要是得出了下面几个结论: 服务器要接收客户端的数据,要建立 socket 内核结构,主要包含两个重要的数据结构, (进程)等待队列 ,和 (数据)接收队列 ,socket 在进程中作为一个文件,可以用文件描述符 fd 来表示,为了方便理解,本文中, socket 内核对象 ≈ fd 文件描述符 ≈ TCP 连接; 阻塞 IO 的主要逻辑是:服务端和客户端建立了连接 socket 后,服务端的用户进程通过 recv 函数接收数据时,如果数据没有到达,则当前的用户进程的进程描述符和回调函数会封装到一个进程等待项中,加入到 socket 的进程等待队列中;如果连接上有数据到达网卡,由网卡将数据通过 DMA 控制器拷贝到内核内存的 RingBuffer 中,并向 CPU 发出硬中断,然后,CPU 向内核中断进程 ksoftirqd 发出软中断信号,内核中断进程 ksoftirqd 将内核内存的 RingBuffer 中的数据根据数据报文的 IP 和端口号,将其拷贝到对应 socket 的数据接收队列中,然后通过 socket 的进程等待队列中的回调函数,唤醒要处理该数据的用户进程; 阻塞 IO 的问题是:一次数据到达会进行 两次进程切换, 一次数据读取有 两处阻塞,单进程对单连接; 非阻塞 IO 模型解决了“ 两次进程切换,两处阻塞,单进程对单连接 ”中的“ 两处阻塞 ”问题,将“ 两处阻塞 ”变成了“ 一处阻塞 ”,但依然存在“