目录

1、操作系统IO原理

2、缓冲区

3、Socket IO模型分类

4、模型演变 Reactor/Proactor

5、select/poll/epoll

6、mmap

7、IO控制方式

7.1、直接程序控制

7.2、中断驱动控制

7.3、直接存储器控制

7.4、通道控制


 

1、操作系统IO原理

用户程序进行IO的读写,基本上会用到read&write两大系统调用。可能不同操作系统,名称不完全一样,但是功能是一样的。read/write系统调用,并不是数据直接与物理设备进行数据交换;read系统调用,是把数据从内核缓冲区复制到进程缓冲区;而write系统调用,是把数据从进程缓冲区复制到内核缓冲区。这个两个系统调用,都不负责数据在内核缓冲区和磁盘之间的交换。底层的读写交换,是由操作系统kernel内核完成的。

2、缓冲区

缓冲区的目的,是为了减少频繁的系统IO调用。大家都知道,系统调用需要保存之前的进程数据和状态等信息,而结束调用之后回来还需要恢复之前的信息,为了减少这种损耗时间、也损耗性能的系统调用,于是出现了缓冲区。

有了缓冲区,操作系统使用read函数把数据从内核缓冲区复制到进程缓冲区,write把数据从进程缓冲区复制到内核缓冲区中。等待缓冲区达到一定数量的时候,再进行IO的调用,提升性能。至于什么时候读取和存储则由内核来决定,用户程序不需要关心。

在linux系统中,系统内核也有个缓冲区叫做内核缓冲区。每个进程有自己独立的缓冲区,叫做进程缓冲区。所以,用户程序的IO读写程序,大多数情况下,并没有进行实际的IO操作,而是在读写自己的进程缓冲区。

3、Socket IO模型分类

同步和异步说的是消息的通知机制,阻塞非阻塞说的是线程的状态

同步阻塞IO:Java BIO

同步非阻塞IO:Java NIO

异步阻塞IO

异步非阻塞IO:Java AIO

IO多路复用:select/poll/epoll,一个线程可以获取到不同的消息连接结果

4、模型演变 Reactor/Proactor

  • 单线程IO:一个while(true)循环不停的接收数据【阻塞后面的接收】
  • 多线程IO:每个接收请求开一个线程【不阻塞后面的接收,线程多之后,CPU开销大】
  • Reactor模型:(Java NIO 网络通信)轮训查询可操作handler

io的作用 java java io底层原理_IO

  • Proactor模型:(Java AIO 网络通信)主动通知可操作handler

按照posix标准,系统IO分为同步IO和异步IO两种,其中同步IO常用的是BIO、NIO。异步IO有AIO。从程序的角度来看,BIO在读和写的时候,会阻塞,只有当程序将流写入操作系统或者读到流后,阻塞才会结束,线程接着run下去。

而NIO和AIO属于非阻塞方式,他们都是基于事件驱动思想,但是NIO采用的是Reactor 模式,而aAIO采用的是Proactor模式。

Reactor 模式使用event loop 阻塞等在IO上,一但IO可以读或写,通过分发器,遍历事件注册队列,将事件分发到指定注册的处理器。由应用的处理器来再将流读取到缓冲区或写入操作系统,完成IO操作。

Proctor 模式下读和写的方法是异步的,只需调用读和写即可。当有流可读取的时候,操作系统会将流传入read方法缓存区,并通知应用程序。对于写,当操作系统将writer 写入完毕时,操作系统会主动通知应用程序。

Proactor模式的AIO,流的读取和写入由操作系统完成,省去了遍历事件通知队列selector 的代价。Windows上的IOCP实现了AIO,linux目前只有基于epoll模拟实现的AIO。

5、select/poll/epoll

select:O(n),它仅仅知道了,有I/O事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。

poll:O(n),poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态, 但是它没有最大连接数的限制,原因是它是基于链表来存储的.

epoll:红黑树,O(1),不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。

6、mmap

mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。mmap在用户空间映射调用系统中作用很大。

当存在客户-服务程序中复制文件时候,其数据流如下,要经历四次数据复制,开销很大。

io的作用 java java io底层原理_外围设备_02

如果采用共享内存的方式,那么将大大优化IO操作,数据流变成了如下,数据只复制两次

io的作用 java java io底层原理_外围设备_03

  • mmap是posix标准下的一个函数,它将文件或设备的访问映射到内存中。函数mmap/munmap
  • windows下使用的是CreateFileMapping和MapViewOfFile,实际上还有一个函数OpenFileMapping,在windows下用这几个函数可以实现不同进程之间的共享内存,同时也要配合同步的对象来实现

7、IO控制方式

7.1、直接程序控制

io的作用 java java io底层原理_外围设备_04

  • 当用户进程需要输入数据时,通过 CPU 向控制器发出一条 I/O 指令,启动设备输入数据,同时把状态寄存器中的忙/闲状态 busy 置为1
  • 用户进程进入测试等待状态,在等待过程中,CPU 不断地用一条测试指令检查外围设备状态寄存器中的 busy 位,而外围设备只有在数据传入控制器的数据寄存器之后,才将该 busy 位置为0,。
  • 处理器将数据寄存器中的数据取出,送入主存指定单元,完成一个字符的I/O操作,接着进行下一个数据的 I/O 操作

直接程序控制方式虽然简单,不需要多少硬件的支持,但由于高速的 CPU 和低速的 I/O 设备之间的速度上不匹配,因此,CPU 与外围设备只能串行工作,使 CPU 的绝大部分时间都处于等待是否完成 I/O 操作的循环测试中,造成 CPU 的极大浪费,外围设备也不能得到合理的使用,整个系统的效率很低。因此,这种I/O控制方式只适合于 CPU 执行速度较慢,且外围设备较少的系统

7.2、中断驱动控制

io的作用 java java io底层原理_io的作用 java_05

由于I/O操作直接由 CPU 控制,每传送一个字符或一个字,都要发生一次中断,仍然占用了大量的 CPU 处理时间,因此可以通过为外围设备增加缓冲寄存器存放数据来减少中断次数。

上述两种方法的特点都是以 CPU 为中心,数据传送通过一段程序来实现,软件的传送手段限制了数据传送的速度。接下来介绍的这两种I/O 控制方式采用硬件的方法来显示 I/O 的控制

7.3、直接存储器控制

DMA(Direct Memory Access)在 DMA 控制器的控制下,采用窃取或挪用总线控制权,在设备和主存之间开辟直接数据交换通道,成批地交换数据,而不必让 CPU 干预

7.4、通道控制

通道控制(Channel Control),独立于 CPU 的专门负责输入输出控制的处理机,它控制设备与内存直接进行数据交换。有自己的通道指令,这些指令由 CPU 启动,并在操作结束时向 CPU 发出中断信号。

通道控制方式与DMA方式类似,也是一种以内存为中心,实现设备和内存直接交换数据的控制方式。与DMA方式不同的是,在DMA方式中,数据的传送方向、存放数据的内存始址以及传送的数据块长度等都由CPU控制,而在通道方式中,这些都由通道来进行控制。另外,DMA方式每台设备至少需要一个DMA控制器,一个通道控制器可以控制多台设备。