举个例子:
假设我们需要编写一个echo服务器程序,功能是:
响应用户从标准输入端键入的命令。接收网络客户端发起的连接请求。这其实是2个不同IO事件,该如何等待请求和处理命令呢?
若服务器在accept中一直等待网络客户端连接,就不能处理输入命令,反过来在read中等待用户键入命令,那就不能接受客户端请求,你会说我们可以使用多进程或多线程,但我们希望不要引起多余的上下文切换,使用单线程来处理可以吗?IO多路复用就是这种解决方法,基本思路是使用select函数,该函数会一直挂起来监听的输入IO集合(文件描述符),只有在一个或多个IO事件发生后(状态变为可读),将控制权给应用程序。服务器使用IO多路复用,借助select函数监测IO事件的发生,当监听的文件描述符变为可读时(IO事件发生),服务器就为相应的状态机执行转移,IO多路复用基本思就是复用单一或少量线程处理多种IO事件,并不是为了更快的处理IO,而是可以同时处理多个IO事件,IO事件可以是标准输入输出、socket等。
生活举例:
好比你去银行柜台,三个窗口只有一个服务员,你去办理业务相当于IO事件,办理业务需要填表,这时相当于你的事件还未准备好,服务员可以接收其他窗口的客户,这种场景相当于服务员一个人(单一进程)监听了三个窗口的事件(IO事件),每次轮询询问每个窗口客户是否填好了表格,没好就去下一个窗口询问,填好了就把这个表格交给后台人员处理,当然你可以每个窗口安排一个服务人员,并发进行,但计算机不同的是,早期的cpu大多是单核cpu,cpu是分配时间片区做到并发的,相当于一个人一天的工作时间是固定比如8小时,2小时喝茶,2小时打王者等等,只不过这个人的手速很快,干起活来在你看来好像是瞬间同时完成的,这人就是cpu他就这么快!多核多cpu那是后来才有的事就另说了。
实现系统IO多路复用的方式有哪些?这里不会介绍每个函数的具体用法,只让读者总体上对多路复用有一个概念模型,深入了解可以参考《深入理解计算机系统》等相关书籍
select函数:系统中的IO被抽象为文件描述符,简单说select函数轮询这些传给他的文件描述符,发现有事件就绪(数据从内核空间到用户空间卡拷贝完成),就将数据读取出来返回给应用程序。
poll函数:和selec函数比较没有本质区别,但没有最大文件描述数量的限制,文件描述符使用链表来存储,上限取决于系统最大文件句柄打开数,select上限是1024个文件描述符
epoll模式:select、poll采用轮询的方式挨个检测每个文件描述符是否就绪,如果描述符太多,每次都要循环查找效率太低,能不能使用回调的方式,当文件描述符就绪时自动回调通知,不用每次循环,从而让复杂度从O(n)降低到O(1) , 这也是epoll和前2个最大的区别,可以理解为event poll,他是一组函数而不是单个函数来实现多路复用的。
应用场景是什么?IO多路复用最大应用场景就是用以设计高并发的事件驱动程序,redis是基于内存的数据库,内部使用IO多路复用处理客户端高并发请求(连接、命令、回复)
和redis一样为了逻辑简洁、高并发、避免锁的竞争和上下文切换, mysql 线程池、nodejs也是基于IO多路复用理念来设计的事件驱动程序。
IO多路复用的优缺点?优点: IO多路复用可以用来设计并发事件驱动程序,相比设计基于多进程、多线程的并发服务器程序来说是比较困难的,利用单线程处理多种IO事件使有限的资源利用最大化,避免多线程的上下文切换、锁的竞争。
缺点:但这样也带来了编码复杂,更大缺点是不能充分体现代多核处理器的优势,更好做法是IO多路复用结合多线程的综合设计理念来设计你的应用程序。
Copyright © 广州京杭网络科技有限公司 2005-2024 版权所有 粤ICP备16019765号
广州京杭网络科技有限公司 版权所有