我们都知道多路复用也是面试的高频考点,如果问到你,你会从那几方面回答呢?
可以参考参考我的回答思路
- 为什么要用I/O多路复用呢
I/O多路复用,是改进多进程/多线程模型; 多进程模型,一个socket就需要一个进程来维护,如果有上千上万个socket那进程的开销过于庞大。多进程模型的工作方式主要是,父子进程,父进程主要是监听socket连接,然后fork一个子进程,会复制父进程的所有东西,比如内存地址、代码片段、堆栈这些,子进程负责处理业务,那这个的上下文切换的开销是巨大的,因为进程的上下文切换,不仅要切换虚拟内存、全局变量、栈等用户空间的资源,还要切换内核堆栈、寄存器等内核资源。 所以有了多线程,一个进程可以有很多线程,那线程的上下文切换开销是小于进程的,线程共享进程空间,发生上下文切换,只用切换线程的私有资源,比如私有数据,寄存器等。但收到一个socket连接,就要创建一个线程,用完就摧毁一个线程,这样的话,线程的创建与销毁开销也蛮大的,于是有了线程池优化,提前创建好线程放入线程池,收到一个socket连接,放入一个队列里,这个队列是全局共享的,所以派一个线程去处理时,是需要加锁的。
但这样也是一个连接对应一个线程,开销也很大。所以有了I/O多路复用,一个进程负责处理多个socket连接。
那你知道I/O多路复用 有哪些实现方式吗
我们都知道I/O多路复用,有三种实现方式,select/poll/epoll;select和poll是相似的,select主要是,接收一个socket连接会放入到一个文件描述符集合里,然后复制到内核空间,又内核空间监听是否有事件发生,内核空间是通过遍历的方式处理,发现有事件发生就会标记一个write/read,然后又复制到用户空间,用户空间通过遍历的方式来处理相关的操作。select的集合主要是BitsMap,是一个固定长度,但poll是一个动态数组,以链表的形式连接。
所有select/poll会涉及两次拷贝、两次遍历。epoll就做了改进,epoll在内核会维护一个红黑树,增删改查都是logn; 那用户有一个socket就会传入内核,加入到红黑树,就不用像select/poll将整个集合复制到内核空间。当红黑树监测到socket有事件发生时,就会通过回调函数加入就绪队列,用户可以通过epoll_wait调用,知道有几个事件发生。事件触发机制主要有两种,边缘触发和水平触发。边缘触发就是,服务端会在就绪队列中苏醒一次,就算服务端没有调用read,也只苏醒一次,所以要保证一次性把就绪队列中数据读取完。水平触发就是,服务端会不断的苏醒,直到你把内核缓冲区里的数据都读取完。
好了I/O多了复用,差不就是这些了