多路复用相关知识

129 阅读2分钟

定义

IO多路复用是一种同步IO模型,实现原理是一个线程监视多个句柄;一旦某个文件句柄就绪,就能够通知应用进行相应的读写操作;没有文件句柄就绪时就会阻塞应用程序,交出CPU。多路指的是网络连接,复用指的是同一个线程。

同步阻塞IO

  • 服务端采用单线程,当accept一个请求后,在recv或send调用阻塞时,将无法accept其他请求(必须等上一个请求处recv或send完),无法处理并发
  • 服务器端采用多线程,当accept一个请求后,开启线程进行recv,可以完成并发处理,但随着请求数增加需要增加系统线程,大量的线程占用很大的内存空间,并且线程切换会带来很大的开销,10000个线程真正发生读写事件的线程数不会超过20%,每次accept都开一个线程也是一种资源浪费.
// 伪代码描述
while(1) {
  // accept阻塞
  client_fd = accept(listen_fd)
  // 开启线程read数据(fd增多导致线程数增多)
  new Thread func() {
    // recv阻塞(多线程不影响上面的accept)
    if (recv(fd)) {
      // logic
    }
  }  
}

同步非阻塞IO

服务器端当accept一个请求后,加入fds集合,每次轮询一遍fds集合recv(非阻塞)数据,没有数据则立即返回错误,每次轮询所有fd(包括没有发生读写事件的fd)会很浪费cpu。

IO多路复用

服务器端采用单线程通过select/epoll等系统调用获取fd列表,遍历有事件的fd进行accept/recv/send,使其能支持更多的并发连接请求

IO多路复用三种实现方式

  • select
  • poll
  • epoll

select

缺点

  • 单个进程所打开的FD是有限制的,通过FD_SETSIZE设置,默认1024
  • 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时开销很大
  • 对socket扫描时是线性扫描,采用轮询的方法,效率较低(高并发时)

poll

缺点

  • 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时开销很大
  • 对socket扫描时是线性扫描,采用轮询的方法,效率较低(高并发时)

epoll

缺点