IO请求底层流程
为了OS的安全性等的考虑,进程是无法直接操作I/O设备的,其必须通过系统调用请求内核来协助完成I/O动作,而内核会为每个I/O设备维护一个buffer。
如下图所示:
整个请求过程为: 用户进程发起请求,内核接受到请求后,从I/O设备中获取数据到buffer中,再将buffer中的数据copy到用户进程的地址空间,该用户进程获取到数据后再响应客户端。
在整个请求过程中,数据输入至buffer需要时间,而从buffer复制数据至进程也需要时间。
记住这两点很重要
1 等待数据准备 (Waiting for the data to be ready)
2 将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)
阻塞I/O (Blocking I/O)
在linux中,默认情况下所有的socket都是blocking,一个典型的读操作流程大概是这样:
当用户进程调用了某个系统调用,内核就开始了IO的第一个阶段:等待数据准备。对于network io来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的UDP包),这个时候内核就要等待足够的数据到来。而在用户进程这边,整个进程会被阻塞。当内核一直等到数据准备好了,它就会将数据从内核中拷贝到用户内存,然后内核返回结果,用户进程才解除block的状态,重新运行起来。
所以,blocking IO的特点就是在IO执行的两个阶段都被block了。
非阻塞I/O (Non-Blocking I/O)
linux下,可以通过设置socket使其变为non-blocking。当对一个non-blocking socket执行读操作时,流程是这个样子:
当用户进程调用某个系统调用时,系统不会阻塞用户进程,而是立刻返回一个ewouldblock错误,从用户进程角度讲 ,并不需要等待,而是马上就得到了一个结果。用户进程判断标志是ewouldblock时,就知道数据还没准备好,于是它就可以去做其他的事了,于是它可以再次发送IO请求,一旦内核中的数据准备好了。并且又再次收到了用户进程的请求,那么它马上就将数据拷贝到了用户内存,然后返回。
IO概念
io流分为输入流及输出流,输入流是文件到内存,输出流是内存到文件。
输入流:把能够读取一个字节序列的对象称为输入流
输出流:把能够写一个字节序列的对象称为输出流
输入输出是相对于内存设备而言的,将外设(硬盘,键盘等)中的数据读取到内存设备
中叫输入;将内存设备中的数据写入到外设中叫输出,所以有读入,写出的称呼:读入到内
存,写出内存。
可以这样比喻:输入流和输出流中间连接着内存,输入流和输出流将读写分离开来进
行操作,先从外设读入内存,然后再写出内存转移到其他外设。
NIO
概念
NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector。传统IO基于字节流和字符流进行操作,而NIO基于Channel和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。
NIO和传统IO(一下简称IO)之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区。
IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变得可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。
Channel
Channel和传统IO中的Stream很相似。虽然很相似,但是有很大的区别,主要区别为:通道是双向的,通过一个Channel既可以进行读,也可以进行写;而Stream只能进行单向操作,通过一个Stream只能进行读或者写。
以下是常用的几种通道:
- FileChannel 通过使用FileChannel可以从文件读或者向文件写入数据,只能使用阻塞模式运行;
- SocketChanel 通过SocketChannel,以TCP来向网络连接的两端读写数据;
- ServerSocketChannel 通过ServerSocketChanel能够监听客户端发起的TCP连接,并为每个TCP连接创建一个新的SocketChannel来进行数据读写;
- DatagramChannel 通过DatagramChannel,以UDP协议来向网络连接的两端读写数据。
FileChannel
Java NIO FileChannel是连接到文件的通道。使用FileChannel,可以从文件中读取数据,并将数据写入文件。 Java NIO FileChannel类是标准Java IO API读取文件的可替代方法。
FileChannel不能设置为非阻塞模式,它只能使用阻塞模式运行
读
//1.打开一个文件通道(FileChannel)需要通过InputStream,OutputStream或RandomAccessFile获取FileChannel。 以下是通过RandomAccessFile打开FileChannel的方法ÿ