套接字知识点:

https://www.cnblogs.com/wmx-learn/p/5312259.html

  1. 有 监听套接字 和 连接套接字 两个概念. listen 返回的是 监听套接字, accept 返回的是 每个client相关的 连接套接字. 大量客户端连接, 会导致 连接套接字增多
  2. socket函数 只是创建了 socket文件描述符, bind 操作是为了给 socket 分配一个端口
  3. socket 有 发送缓冲区、接受缓存区、等待列表

epoll

https://mp.weixin.qq.com/s/MzrhaWMwrFxKT7YZvd68jw

  1. 硬件层: 网线接收到数据写入网卡, 网卡将数据写入内存,同时发起中断, cpu收到中断后进行处理
  2. socket阻塞与唤醒: 进行recv, 没有数据的情况下, 操作系统 会将进程 注册到 socket的等待列表, 同时将 进程从 活动列表 添加到 阻塞列表。然后, 有数据的到来, socket会唤醒等待列表的进程, 将进程放到工作队列, 这个时候进程recv的时候 就可以拿到数据了
  3. 所以, 阻塞和唤醒的本质就是: 注册到等待列表, 和 从等待列表中删除.
  4. 注意: 唤醒操作,就是讲 进程从 等待队列 放到了 工作队列
  5. 当大量客户端连接的时候, 服务端会有大量的 连接套接字, 这时候 recv的操作,会导致 进程 大量注册 socket的等待列表, 以及 唤醒的时候, 大量的从等待列表中删除、多次唤醒.
  6. 问题来了, 因为每次唤醒的时候, 进程是不知道是 哪个 socket唤醒的, 因此多了一次 遍历socket的过程.
  7. select: 逻辑和上面类似, 传递一个[]fd作为要监视的socket, 在唤醒的时候 进行一个遍历, 判断哪个socket有数据
  8. epoll: 简化了逻辑, 只需要传递一次 等待列表 (监听的socket很少改变?), 同时维护一个唤醒列表 来避免唤醒后的遍历.
  9. epoll 使用 双链列表维护 就绪列表, 使用红黑树 维护 监视的socket.