二、I/O模型
1、概念理论
(1)、阻塞调用与非阻塞调用
①、阻塞调用是指调用结果返回之前,当前线程会被挂起,调用线程只有在得到结果之后才会返回。
②、非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
③、两者的最大区别在于被调用方在收到请求到返回结果之前的这段时间内,调用方是否一直在等待。阻塞是指调用方一直在等待而且别的事情什么都不做。非阻塞是指调用方先去忙别的事情。
(2)、同步处理与异步处理
①、同步处理是指被调用方得到最终结果之后才返回给调用方。
②、异步处理是指被调用方先返回应答,然后再计算调用结果,计算完最终结果后再通知并返回给调用方。
(3)、阻塞、非阻塞和同步、异步的区别
阻塞、非阻塞和同步、异步其实针对的对象是不一样的:
①、阻塞、非阻塞的讨论对象是调用者
②、同步、异步的讨论对象是被调用者
2、阻塞式I/O模型(Blocking I/O)
(1)、简介
①、每个请求都需要独立的线程完成数据Read,业务处理,数据Write的完整操作问题。
②、当并发数较大时,需要创建大量线程来处理连接,系统资源占用较大。
③、连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在Read操作上,造成线程资源浪费。
(2)、流程图
(3)、流程图
①、第一阶段:指磁盘把数据装载到内核的内存中空间中。
②、第二阶段:指内核的内存空间的数据copy到用户的内存空间(这个才是真实I/O操作)。
(4)、比喻
一个人在钓鱼,当没鱼上钩时,就坐在岸边一直等。
3、非阻塞式I/O模型(Non-Blocking I/O)
(1)、简介
在非阻塞式I/O模型中,应用程序把一个套接口设置为非阻塞就是告诉内核,当所请求的I/O操作无法完成时,不要将进程睡眠,而是返回一个错误,应用程序基于I/O操作函数将不断的轮询数据是否已经准备好,如果没有准备好,继续轮询,直到数据准备好为止。
(2)、流程图
(3)、流程图
①、第一阶段:指磁盘把数据装载到内核的内存中空间中。
②、第二阶段:指内核的内存空间的数据copy到用户的内存空间(这个才是真实I/O操作)。
(4)、优缺点
①、优点:不会阻塞在内核的等待数据过程,每次发起的I/O请求可以立即返回,不用阻塞等待,实时性较好。
②、缺点:轮询将会不断地询问内核,这将占用大量的CPU时间,系统资源利用率较低,所以一般Web服务器不使用这种I/O模型。
(5)、比喻
边钓鱼边玩手机,隔会再看看有没有鱼上钩,有的话就迅速拉杆。
4、I/O复用模型(I/O multiplexing)
Netty的非阻塞I/O的实现关键是基于I/O复用模型。在I/O复用模型中,会用到select或poll函数或epoll函数(Linux2.6以后的内核开始支持),这两个函数也会使进程阻塞。跟阻塞I/O所不同的是这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。
(1)、简介
①、Netty的IO线程NioEventLoop由于聚合了多路复用器Selector,可以同时并发处理成百上千个客户端连接。
②、当线程从某客户端Socket通道进行读写数据时,若没有数据可用时,该线程可以进行其他任务。
③、线程通常将非阻塞I/O的空闲时间用于在其他通道上执行I/O操作,所以单个线程可以管理多个输入和输出通道。
④、由于读写操作都是非阻塞的,这就可以充分提升I/O线程的运行效率,避免由于频繁I/O阻塞导致的线程挂起。
⑤、一个I/O线程可以并发处理N个客户端连接和读写操作,这从根本上解决了传统同步阻塞I/O一连接一线程模型,架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。
⑥、基于Buffer
a、传统的BIO是面向字节流或字符流的,以流式的方式顺序地从一个Stream中读取一个或多个字节, 因此也就不能随意改变读取指针的位置。
b、在NIO中,抛弃了传统的I/O流,而是引入了Channel和Buffer的概念。在NIO中,只能从Channel中读取数据到Buffer中或将数据从Buffer中写入到Channel。
c、基于Buffer操作不像传统IO的顺序操作,NIO中可以随意地读取任意位置的数据。
(2)、流程图
(3)、流程图
①、第一阶段:指磁盘把数据装载到内核的内存中空间中。
②、第二阶段:指内核的内存空间的数据copy到用户的内存空间(这个才是真实I/O操作)。
(4)、优缺点
①、优点:可以基于一个阻塞对象,同时在多个描述符上等待就绪,而不是使用多个线程(每个文件描述符一个线程),这样可以大大节省系统资源。
②、缺点:当连接数较少时效率相比多线程+阻塞I/O模型效率较低,可能延迟更大,因为单个连接处理需要2次系统调用,占用时间会有增加。
(5)、比喻
放了一堆鱼竿,在岸边一直守着这堆鱼竿,直到有鱼上钩。
5、信号驱动式I/O模型(signal-driven I/O)
(1)、简介
在信号驱动式I/O模型中,应用程序使用套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。
(2)、流程图
(3)、流程图
①、第一阶段:指磁盘把数据装载到内核的内存中空间中。
②、第二阶段:指内核的内存空间的数据copy到用户的内存空间(这个才是真实I/O操作)。
(4)、缺点
①、信号I/O在大量IO操作时可能会因为信号队列溢出导致没法通知。
②、信号驱动I/O尽管对于处理UDP套接字来说有用,即这种信号通知意味着到达一个数据报,或者返回一个异步错误。但是,对于TCP而言,信号驱动的I/O方式近乎无用,因为导致这种通知的条件为数众多,每一个来进行判别会消耗很大资源,与前几种方式相比优势尽失。
(5)、比喻
鱼竿上系了个铃铛,当铃铛响,就知道鱼上钩,然后可以专心玩手机。
6、异步I/O模型(asynchronous I/O)
(1)、简介
①、由POSIX规范定义,应用程序告知内核启动某个操作,并让内核在整个操作(包括将数据从内核拷贝到应用程序的缓冲区)完成后通知应用程序。
②、这种模型与信号驱动模型的主要区别在于:信号驱动I/O是由内核通知应用程序何时启动一个I/O操作,而异步I/O模型是由内核通知应用程序I/O操作何时完成。
(2)、流程图
(3)、流程图
无论第一第二段, 不再向系统调用提出任何反馈, 只有数据完全复制到服务进程内存中后, 才向服务进程返回ok的信息,其它时间进程可以随意做自己的事情,直到内核通知ok信息。
①、注意: 只在文件中可以实现AIO, 网络异步IO 不可能实现
②、第一阶段:指磁盘把数据装载到内核的内存中空间中。
③、第二阶段:指内核的内存空间的数据copy到用户的内存空间(这个才是真实I/O操作)。
(4)、优缺点
①、优点:异步I/O能够充分利用DMA特性,让I/O操作与计算重叠。
②、缺点:要实现真正的异步I/O,操作系统需要做大量的工作。目前Windows下通过IOCP实现了真正的异步I/O,而在Linux系统下,Linux2.6才引入,目前AIO并不完善,因此在Linux下实现高并发网络编程时都是以IO复用模型模式为主。
7、五种I/O模型比较
(1)、同步阻塞:两个阶段都是阻塞的,所有数据准备完成后,才响应。
(2)、同步非阻塞:磁盘从磁盘复制到内核内存中的时候, 不停循环访问内核数据是否准备完成,性能有可能更差 ,看上去他可以做别的事情了, 但是其实他在不停的循环。
(3)、同步IO:如果第二段是阻塞的,代表是同步的,BIO、NIO、IO复用、事件驱动都是同步的。
(4)、异步IO:内核后台自己处理 ,把大量时间拿来处理用户请求 。
(5)、越往后,阻塞越少,理论上效率也是最优。其五种I/O模型中,前四种属于同步I/O,因为其中真正的I/O操作(recvfrom)将阻塞进程/线程,只有异步I/O模型才于POSIX定义的异步I/O相匹配。
参考资料记录:Netty基础概念_L_D_Y_K的博客-CSDN博客