想成为大牛,不得不懂的五种Linux网络IO模型

前言你知?.NETty为什么性能这么高吗?你知道redis为什么单线程如此之快吗?这都和底层的网络IO模型有关系,所以掌握网络IO模型真的很重要,是一个基础,对你更好的理解其他应用帮助非常大,今天我们就好好来聊聊linux的5种网络IO模型 。
IO工作原理我们的应用大多数情况都是部署在linux系统中,linux系统也是一种应用,它是基于计算机硬件的一种操作系统软件 。当我们接收一次网络传输,计算机硬件的网卡会从网络中将读到的字节流写到linux的buffer缓冲区内存中,然后用户空间会调用linux对外暴露的接口,将linux内核空间buffer内存中的数据拷贝到用户空间的buffer区 。这一次网络读取就是磁盘IO,同理从磁盘中读取,也是遵循一样的机制 。

想成为大牛,不得不懂的五种Linux网络IO模型

文章插图
IO的性能瓶颈主要是下面两个阶段:
  • 准备阶段,指数据从网络网卡或本地存储器读取到内核的过程
  • 复制阶段,指将内核缓冲区中的数据拷贝至用户态的进程缓冲区
所以,Linux系统中提供了五种IO模型来提高性能,它们分别为BIO、NIO、多路复用、信号驱动、AIO,从性能上来说,它们属于依次递进的关系,但越靠后的IO模型实现也越为复杂 。
1. 阻塞IO模型BIO当用户应用线程调用linux操作系统的recvfrom?函数读取数据的时候,如果内核的buffer?内存中没有数据,那么用户线程会阻塞等待,直到内核的buffer?内存中有数据了,才去将内核的buffer内存中的数据拷贝到用户应用内存中 。
想成为大牛,不得不懂的五种Linux网络IO模型

文章插图
打比方理解:
比如你给女神发一条短信, 说我来找你了, 然后就默默的一直等着女神下楼, 这个期间除了等待你不会做其他事情, 属于备胎做法 。也就是说线程会一直阻塞等待内核把数据准备好,然后将数据copy到用户空间 。
优点:
  • 开发简单,容易入门;
  • 在阻塞等待期间,用户线程挂起,在挂起期间不会占用CPU资源 。
缺点:
  • 在BIO这种模型中,为了支持并发请求,通常会采用多线程的方式,并发过高时会导致创建大量线程,造成频繁的上下文切换,甚至系统崩溃
在JAVA常用的Tomcat服务器中,Tomcat7.x版本以下默认的IO类型也是BIO,但是Tomcat中对BIO模型稍微进行了优化,通过线程池做了限制,所以避免出现并发过高而系统崩溃的情况 。
2. 非阻塞IO模型NIO当用户应用线程调用linux操作系统的recvfrom?函数读取数据的时候,如果内核的buffer?内存中没有数据,那么用户线程会直接拿到结果(没有数据)不会阻塞等待,于是又会发起一次recvfrom函数调用,直到内核的buffer内存中有数据了,才去将内核的buffer内存中的数据拷贝到用户应用内存中 。
在非阻塞IO模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞IO不会交出CPU,而会一直占用CPU 。
想成为大牛,不得不懂的五种Linux网络IO模型

文章插图
打比方理解:
比如你给女神发短信, 如果不回, 接着再发, 一直发到女神下楼, 这个期间你除了发短信等待不会做其他事情, 属于专一做法 。同理,用户线程无需等待内核数据准备结果,直接返回,然后通过轮询去问结果,如果结果为准备好,进程把数据copy到用户空间 。
优点:
  • 每次发起IO调用,在内核等待数据的过程中可以立即返回,用户线程不会阻塞 。
缺点:
  • 多个线程不断轮询内核是否有数据,会占用大量CPU时间 。
NIO相对来说较为鸡肋,因此目前大多数的NIO技术并非采用这种多线程的模型,而是基于单线程的多路复用模型实现的,Java中支持的NIO模型亦是如此 。
3. 多路复用IO模型前面提到NIO由于线程在不断的轮询查看数据是否准备就绪,造成CPU开销较大 。既然说是由于大量无效的轮询造成CPU占用过高,那么等内核中的数据准备好了之后,再去询问数据是否就绪是不是就可以了?答案是Yes 。这就是我们多路复用IO模型的设计思想 。
多路复用IO模型是基于文件描述符File Descriptor?实现的,文件描述符是打开现存文件或新建文件时,内核会返回一个文件描述符 。读写文件也需要使用文件描述符来指定待读写的文件 。文件包含音频文件,常规文件,硬件设备等等,也包括网络套接字(Socket) 。


推荐阅读