IO线程模型 |
您所在的位置:网站首页 › read非阻塞读文件 › IO线程模型 |
注:本文内容部分摘自《Netty 4 核心原理与手写 RPC 实践》 1. IO 简介在 UNIX 的设计哲学中,一切皆文件。文件就是一串二进制流。对文件的操作包括有:读、写、打开、关闭 等。计算机对这些流进行数据的收发操作,简称为 IO(Input and Output)操作。 IO 的种类:内存 IO、网络IO、磁盘IO。 2. IO 模型 2.1 基本概念 2.1.1 同步 vs 异步同步和异步是指 CPU 时间片的使用,主要看请求发起方对消息结果的获取是主动发起的,还是被动通知的。如果是服务方在处理完成后主动告知请求方,那么就是异步。异步通知的方式一般通过状态改变、消息通知或者回调函数来完成,大多时候采用的都是回调函数。 2.1.2 阻塞 vs 非阻塞阻塞和非阻塞通常指对 IO 的操作。阻塞就是当我们发起一个请求调用时,在请求结果返回之前,如果当前线程是处于挂起状态,那么就是阻塞。如果是运行状态,那么就是非阻塞。 2.2 IO 模型 2.2.1 同步阻塞应用程序发起调用 recvfrom -> 内核准备数据->数据就绪->数据从内核拷贝到用户空间。 特点在 I/O 执行的两个阶段(等待数据和拷贝数据)都被阻塞典型应用Java BIO 、阻塞 Socket优点1. 进程阻塞挂起不消耗 CPU 资源 2. 实现难度低 3. 适合并发量小的网络应用开发缺点1. 不适合并发量大的应用,因为一个请求 I/O 会阻塞进程 2. 需要为每个请求分配一个处理进程(线程)以及时响应,系统开销大在阻塞 I/O 的场景中,当调用 InputStread.read() 方法时是阻塞的,它会一直等到数据到来(或超时)时才会返回;同样,在调用 SeverSocket.accept() 方法时,也会一直阻塞到有客户端连接才会返回,每个客户端连接成功后,服务端都会启动一个线程去处理该客户端的请求。 阻塞 I/O 的缺点: 当客户端多时,会创建大量的处理线程。且每个线程都要占用栈空间和一些 CPU 时间。阻塞可能带来频繁的上下文切换,且大部分上下文切换可能是无意义的。![]() 应用程序发起调用 recvfrom -> 内核返回数据准备结果 -> 重复 recvfrom… -> 数据就绪->数据从内核拷贝到用户空间。 相较于同步阻塞,同步非阻塞会在被调用之后即刻返回一个结果。当用户进程发起 read 操作时,如果内核中的数据还没有准备好,那么它不会阻塞进程而是立刻返回一个 error。 特点用户进程需要不断地主动询问内核(Kernel)数据是否已准备好典型应用Socket 设置成 NON_BLOCK优点1. 实现难度,开发应用相较于阻塞 I/O 模型较难缺点1. 进程轮询(重复)调用,消耗 CPU 资源 2. 适合并发量较小且不需要及时响应的网络应用开发 2.2.3 IO 多路复用多个进程可以注册到一个 Selector(复用器)上,当用户进程调用该 Selector,Selector 会监听注册进来的所有 I/O,如果 Selector 监听的所有 I/O 在内核缓冲区都没有可读数据,select 调用进程会被阻塞,而当任一 I/O 在内核缓冲区有可读数据时,select 调用就会返回,而后 select 调用进程可以自己或通知另外的进程再次发起读取 I/O,读取内核中准备好的数据。多个进程注册 I/O 后,只有一个 select 调用进程被阻塞。 特点对于每一个 Socket,一般都设置成非阻塞,但是整个用户的进程其实是一直被阻塞的,只不过进程是被 select 函数阻塞,而不是被 Socket I/O 阻塞典型应用Java NIO优点1. 专一进程解决多个进程 I/O 的阻塞问题,性能好,Reactor 模式 2. 适合高并发服务应用开发,一个进程/线程处理多个请求缺点1. 实现和开发应用难度大目前流行的多路复用 I/O 的实现主要包括四种:select、poll、epoll、kqueue。 多路复用 I/O 技术最适用的是『高并发』场景,所谓『高并发』是指 1ms 内至少同时有上千个连接请求准备好。 ![]() 异步 IO 的工作机制:用户进程发起 aio_read 操作,给内核传递与 read 相同的描述符、缓冲区指针、缓冲区大小三个参数及文件偏移,告诉内核当整个操作完成时,如何通知我们立刻就可以开始去做其他的事。 特点真正实现了 异步 IO典型应用Java7 AIO优点1. 不阻塞,采用 Proactor 模式 2. 非常适合高性能、高并发服务应用缺点1. 实现和开发应用难度大 2. 需要操作系统的底层支持,Linux 2.5 内核首现,Linux 2.6 产品的内核标准特性 2.3 Java BIO 到 NIO 到 AIO IO 模型BIONIO通信面向流面向缓冲区处理阻塞非阻塞触发无选择器 2.3.1 面向流 vs 面向缓冲区 面向流:在面向流 IO 系统中,所有数据都是直接写入或者直接将数据读取到 Stream 对象中。每次从流中读取一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。面向缓冲区:在面向缓冲区 IO 系统中,所有数据都是用缓冲区处理的。在数据读取或写入时,都是直接读取或写入缓冲区。 在 NIO 中,所有的缓冲区类型都继承于抽象类 Buffer,最常用的是 ByteBuffer。 缓冲区实际上是一个容器对象,其本质是一个特殊的数组。缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化。在缓冲区种,最重要的属性有以下三个。 position:指定下一个要被写入或读取的元素索引。limit:指定还有多少数据需要取出/放入。capacity: 指定了可以存储在缓冲区中的最大数据容量。阻塞:当一个线程调用 read() 或 write() 时,该线程被阻塞,直到有一些数据被读出,或者数据完全写入。该线程在此期间不能再做任何事情。 非阻塞:一个线程从某通道(Channel)发送请求读取/写入数据,这个线程同时可以去做其他事情。线程通常将非阻塞 IO 的空闲时间用于在其他通道上执行 IO 操作,所以一个单独的线程现在可以管理多个通道。 选择器(Selector)允许一个线程监听多个通道。可以注册多个通道使用一个选择器,然后使用一个单独的线程来选择通道。 NIO 可以只使用一个线程来管理多个通道(网络连接或文件)。 JDK 1.7 开始实现了真正的异步 AIO、把 IO 读写操作完全交给操作系统,学习了 Linux Epoll 模式。 在多路 IO 复用模型中,事件循环将文件句柄的状态事件通知给用户线程,由用户线程自行读取数据、处理数据。而在 AIO 模型中,当用户线程收到通知时,数据已经被内核读取完毕,并放在了用户线程指定的缓冲区内,内核在 IO 完成后通知用户线程直接使用即可。 AIO 模型使用 Proactor 设计模式实现这一机制。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |