无论是阻塞IO还是非阻塞IO,用户应用在一阶段都需要调用recvfrom来获取数据,差别在于无数据时的处理方案:
- 如果调用recvfrom时,恰好没有数据,阻塞IO会使进程阻塞,非阻塞IO使CPU空转,都不能充分发挥CPU的作用。
- 如果调用recvfrom时,恰好有数据,则用户进程可以直接进入第二阶段,读取并处理数据。
比如 服务端处理客户端Socket请求时,在单线程情况下,只能依次处理每一个Socket,如果正在处理的Socket恰好未就绪(数据不可读或不可写),线程就会被阻塞,所有其他客户端socket都必须等待,性能自然会很差。
多线程确实是可以提高效率,但也不是绝对的,因为CPU在多个线程间的上下文切换开销也很大,如果线程过多,反而会降低效率。
用户进程如何知道内核中数据是否就绪呢?
文件描述符(Flie Descriptor):简称FD,是一个从0开始递增的无符号整数,用来关联Linux中的一个文件。在Linux中,一切皆文件,例如常规文件、视频、硬件设备等,当然也包括网路套接字(Socket)。
IO多路复用:是利用单个线程来同时监听多个FD,并在某个FD可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。
与阻塞IO区别在哪里呢?区别在IO多路复用调用的是select命令,它包含的是对多个FD的监听,一旦有一个或者多个FD就绪了,就立马返回,然后用户态再调用recvfrom,准确的调用就绪的FD,如果多个FD都没有就绪,也会阻塞等待,但是这种可能性很小,但凡等待过程中有一个FD就绪了,就立马进行下一步了。
监听FD的方式、通知的方式又有多种实现,常见的有:
- Select
- Poll
- Epoll
差异:
- select和poll只会通知用户进程有FD就绪,但不确定具体是哪个FD,需要用户进程逐个遍历FD来确认
- epoll则会再通知用户进程FD就绪的同时,把已就绪的FD写入用户空间