IO多路复用:Select, Poll与Epoll

参考:EPOLL原理详解(图文并茂)

一、网络数据是如何被接收的

  1. 网卡接收到网络数据
  2. 将网络数据暂存到内存当中(DMA传输技术)
  3. 网卡向CPU发起硬件中断
  4. CPU执行中断处理函数,将内存中的数据存储到socket文件描述符中,并唤醒相关进程

Q1: 如何知道要将网络数据存储到哪个socket文件中?
A1: 每个socket都由(IP地址:端口号)唯一标识

Q2: Socket文件由哪几个部分组成?
A2: 输入缓冲区、输出缓冲区和等待队列(存放等待该文件的进程引用)

二、IO多路复用

思想:用一个进程/线程同时监听多个IO操作(如socket),直到它们中的一个或多个准备就绪
优点:

  • 允许单个进程处理大量连接,减少了内存和CPU资源的消耗
  • 减少系统调用的次数,因为一次系统调用可以检查多个IO操作
  • 减少了线程\进程的数量,因此可以减少上下文切换的次数

三、Select和Poll

Select的工作流程:

  1. 在用户态维护一个监听队列(数组),用于存放需要监听的socket
  2. Select系统调用,将当前进程插入所有socket的等待队列后阻塞
  3. 触发中断程序后,将原进程从所有socket的等待队列移除并唤醒
  4. 用户态进程遍历数组找到触发IO的socket并执行相应的操作

缺点:

  • 每次Select系统调用都需要遍历两次队列(将进程插入和删除等待队列)
  • 每次Select系统调用都需要将整个socket数组传递给内核

Poll与Select的主要区别在于使用链表存储需要监听的socket

四、Epoll

Epoll的工作流程:

  1. 在内核中维护eventpoll对象,包含一个监听队列(红黑树)和一个就绪队列(双向链表)
  2. 使用epoll_ctl系统调用向监听队列中添加或删除所要监听的socket
  3. 当socket触发IO事件时,中断程序将socket引用加入就绪队列中,并不直接唤醒进程
  4. 使用epoll_wait系统调用将当前进程插入到eventpoll的等待队列中等待就绪队列的返回
  5. 用户态对准备就绪的socket进行操作