IO多路复用:Select, Poll与Epoll
一、网络数据是如何被接收的
- 网卡接收到网络数据
- 将网络数据暂存到内存当中(DMA传输技术)
- 网卡向CPU发起硬件中断
- CPU执行中断处理函数,将内存中的数据存储到socket文件描述符中,并唤醒相关进程
Q1: 如何知道要将网络数据存储到哪个socket文件中?
A1: 每个socket都由(IP地址:端口号)唯一标识
Q2: Socket文件由哪几个部分组成?
A2: 输入缓冲区、输出缓冲区和等待队列(存放等待该文件的进程引用)
二、IO多路复用
思想:用一个进程/线程同时监听多个IO操作(如socket),直到它们中的一个或多个准备就绪
优点:
- 允许单个进程处理大量连接,减少了内存和CPU资源的消耗
- 减少系统调用的次数,因为一次系统调用可以检查多个IO操作
- 减少了线程\进程的数量,因此可以减少上下文切换的次数
三、Select和Poll
Select的工作流程:
- 在用户态维护一个监听队列(数组),用于存放需要监听的socket
- Select系统调用,将当前进程插入所有socket的等待队列后阻塞
- 触发中断程序后,将原进程从所有socket的等待队列移除并唤醒
- 用户态进程遍历数组找到触发IO的socket并执行相应的操作
缺点:
- 每次Select系统调用都需要遍历两次队列(将进程插入和删除等待队列)
- 每次Select系统调用都需要将整个socket数组传递给内核
Poll与Select的主要区别在于使用链表存储需要监听的socket
四、Epoll
Epoll的工作流程:
- 在内核中维护eventpoll对象,包含一个监听队列(红黑树)和一个就绪队列(双向链表)
- 使用epoll_ctl系统调用向监听队列中添加或删除所要监听的socket
- 当socket触发IO事件时,中断程序将socket引用加入就绪队列中,并不直接唤醒进程
- 使用epoll_wait系统调用将当前进程插入到eventpoll的等待队列中等待就绪队列的返回
- 用户态对准备就绪的socket进行操作