定义
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
缺点
- 只能在工作在linux下 参考文章 : zhuanlan.zhihu.com/p/150972878