IO多路复用
无论是阻塞l0还是非阻塞lO,用户应用在一阶段都需要调用recvfrom来获取数据,差别在于无数据时的处理方案:
*如果调用recvfrom时,恰好没有数据,阻塞IO会使进程阻塞,非阻塞IlO使CPU空转,都不能充分发挥CPU的作用。
*如果调用recvfrom时,恰好有数据,则用户进程可以直接进入第二阶段,读取并处理数据
比如服务端处理客户端Socket请求时,在单线程情况下,只能依次处理每一个socket,如果正在处理的socket恰好未就绪(数据不可读或不可写),线程就会被阻塞,所有其它客户端socket都必须等待,性能自然会很差。
这就像服务员给顾客点餐,分两步:
1,顾客思考要吃什么(等待数据就绪)
2,顾客想好了,开始点餐(读取数据)
要提高效率有几种办法?
方法一:增加服务员(多线程),缺点:切换上下文开销也很大
方法二:不排队,谁想好吃什么了(数据就绪了),服务员就给谁点餐(用户应用就去读取数据)
那么问题来了,用户进程如何知道谁就绪了呢?
文件描述符(File Descriptor):简称FD,是一个从零开始递增的无符号整数,用来关联linux中的一个文件。在Linux中一切皆文件,例如常规文件,视频,硬件设备等,当然也包括网络套接字(socket)。
IO多路复用就是:利用单个线程来监听多个FD,并在某个FD可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。
与BIO(阻塞IO)和NIO(非阻塞IO)不同的是,BIO和NIO在调用内核态时,都是直接调用recvfrom方法,但是在调用时,用户空间是不知道内核空间的数据是否就绪的,如果数据没有就绪就会造成阻塞。
所以,多路IO会先调用select方法同时监听多个sockets,并阻塞等待数据(如果数据都没就绪的话),一旦内核空间有数据就绪了,内核空间会通知用户空间有数据就绪了,再去调用recvfrom方法。
不过监听FD的方式、通知的方式又有多种实现,常见的有,select,poll,epoll。
select和poll只会通知有FD就绪,但不确定具体是哪个FD,需要用户进程逐个遍历FD来确认。
epoll则会在通知用户进程FD就绪的同时,把已经就绪的FD写入用户空间。
IO多路复用-Epoll
FD是以红黑树形式存储在内核空间的,就绪的FD会以链表形式存储。
将一个FD添加到红黑树中时,会设置一个回调函数,ep_poll_callback监听FD,callback触发时,就把对应的FD加入到rdlist这个就绪列表中。
Select,Poll,Epoll的总结:
Select模式的三大问题:
能监听的FD最大不超过1024
每次select都要把所有要监听的FD都拷贝到内核空间
每次都要遍历所有的FD来判断就绪状态
Poll模式的问题:
poll利用了链表来解决select中监听上限的问题,但依然要遍历所有的FD,如果监听太多,性能会下降,理论上来讲是无限的,但是实际使用中不允许很多的FD。
Epoll模式是如何解决上述问题的?
基于epoll实例中的红黑树保存要监听的FD,理论上无上限,而且增删改查效率都非常高,性能不会随监听的FD数量多而变差。
每个FD只需要执行一个epoll_ctl添加到红黑树,以后每次epol_wait无需传递任何参数,无需重复拷贝FD到内核空间。
内核会将就绪的FD直接拷贝到用户空间的指定位置,用户进程无需遍历所有FD就能知道就绪的FD是谁。
标签:多路复用,Redis,阻塞,FD,内核,IO,就绪,监听 From: https://www.cnblogs.com/tyleaf/p/17107483.html