「这是我参与2022首次更文挑战的第13天,活动详情查看:2022首次更文挑战」。
说起 epoll,我们需要先提一下同步阻塞的网络 io 模型。
什么是同步阻塞模型?服务器接收到一个网络请求,会依次采用如下流程进行处理:
- 创建 socket,等待接收数据,将进程 id 放入 socket 等待队列中
- 数据包抵达网卡
- 网卡把数据DMA到内存
- 网卡硬中断通知 cpu
- cpu 发出软中断给 ksoftirqd 内核进程进行处理
- ksoftirqd 内核进程从内存 ringbuffer 摘下数据包送到1创建的 socket 队列中,唤醒 socket 等待队列中的进程进行处理
- socket 进行后续处理
它有如下几个缺点:
- 一个连接占用一个进程,并发的情况下需要启用大量进程,消耗资源;
- 没有数据/数据没到的情况下需要将cpu的控制权交出,即需要频繁切换进程,消耗资源;
从上面可以看到,一个进程只处理一个连接,那么有没有一个进程可处理多个连接的方法?有,epoll就是其中一个。方法就是当有一个连接有数据到来,能第一时间知道。
与epoll相关的函数有三个:
- epoll_create: 创建 epoll 对象
- epoll_ctl: 向 epoll 中添加连接
- epoll_wait: 等待连接上的 io 事件
详细来说,流程如下:
- 创建 epoll 对象,epoll 对象主要由就绪队列、socket 红黑树、等待队列等组成
- 添加 socket 到 epoll 对象中
- 检查就绪队列,如果队列为空,就阻塞住,然后把 cpu 的控制权交给其它进程
- 如果有数据到达,那么 cpu 触发软中断,那么将 socket 送入到 epoll 的就绪队列
- 唤醒用户进程,检查就绪队列,通过红黑树查找到数据所属的 socket,并进程处理
可以看到,epoll 非常高效,可以实现一个进程监听多个连接,所以在网络服务器中有非常广泛的应用。