基本介绍
IO 多路转接也称为 IO 多路复用,它是一种网络通信的手段(机制),通过这种方式可以同时监测多个文件描述符并且这个过程是阻塞的,一旦检测到有文件描述符就绪( 可以读数据或者可以写数据)程序的阻塞就会被解除,之后就可以基于这些(一个或多个)就绪的文件描述符进行通信了。通过这种方式在单线程 / 进程的场景下也可以在服务器端实现并发。常见的 IO 多路转接方式有:select、poll、epoll。
多线程 / 多进程并发
- 主线程 / 父进程:调用 accept() 监测客户端连接请求
- 如果没有新的客户端的连接请求,当前线程 / 进程会阻塞
- 如果有新的客户端连接请求解除阻塞,建立连接
子线程 / 子进程:和建立连接的客户端通信
- 调用 read() / recv() 接收客户端发送的通信数据,如果没有通信数据,当前线程 / 进程会阻塞,数据到达之后阻塞自动解除
- 调用 write() / send() 给客户端发送数据,如果写缓冲区已满,当前线程 / 进程会阻塞,否则将待发送数据写入写缓冲区中
IO 多路转接并发
使用 IO 多路转接函数委托内核检测服务器端所有的文件描述符(通信和监听两类),这个检测过程会导致进程 / 线程的阻塞,如果检测到已就绪的文件描述符阻塞解除,并将这些已就绪的文件描述符传出
根据类型对传出的所有已就绪文件描述符进行判断,并做出不同的处理
监听的文件描述符:和客户端建立连接
此时调用 accept() 是不会导致程序阻塞的,因为监听的文件描述符是已就绪的(有新请求)
通信的文件描述符:调用通信函数和已建立连接的客户端通信
- 调用 read() / recv() 不会阻塞程序,因为通信的文件描述符是就绪的,读缓冲区内已有数据
- 调用 write() / send() 不会阻塞程序,因为通信的文件描述符是就绪的,写缓冲区不满,可以往里面写数据
对这些文件描述符继续进行下一轮的检测(循环往复。。。)
答案:
Select低层基于链表实现只运行在linux 和windows Mac平台,无文件描述符时阻塞,文件描述符来了解除阻塞。返回通知文件描述符数量。你并不告知哪些文件描述符已经就绪。一般基本1024个数量
poll 的机制与 select 类似,与 select 在本质上没有多大差别,使用方法也类似,下面的是对于二者的对比
- 内核对应文件描述符的检测也是以线性的方式进行轮询,根据描述符的状态进行处理
- poll 和 select 检测的文件描述符集合会在检测过程中频繁的进行用户区和内核区的拷贝,它的开销随着文件描述符数量的增加而线性增大,从而效率也会越来越低。
- select检测的文件描述符个数上限是1024,poll没有最大文件描述符数量的限制
- select可以跨平台使用,poll只能在Linux平台使用
终极考察大招epoll
epoll 全称 eventpoll,是 linux 内核实现 IO 多路转接 / 复用(IO multiplexing)的一个实现。IO 多路转接的意思是在一个操作里同时监听多个输入输出源,在其中一个或多个输入输出源可用的时候返回,然后对其的进行读写操作。epoll 是 select 和 poll 的升级版,相较于这两个前辈,epoll 改进了工作方式,因此它更加高效
- 对于待检测集合select和poll是基于线性方式处理的,epoll是基于红黑树来管理待检测集合的。
- select和poll每次都会线性扫描整个待检测集合,集合越大速度越慢,epoll使用的是回调机制,效率高,处理效率也不会随着检测集合的变大而下降
- select和poll工作过程中存在内核/用户空间数据的频繁拷贝问题,在epoll中内核和用户区使用的是共享内存(基于mmap内存映射区实现),省去了不必要的内存拷贝。
- 程序猿需要对select和poll返回的集合进行判断才能知道哪些文件描述符是就绪的,通过epoll可以直接得到已就绪的文件描述符集合,无需再次检测
- 使用 epoll 没有最大文件描述符的限制,仅受系统中进程能打开的最大文件数目限制