一、原理
1.用户态
对操作系统而言,JVM只是一个用户进程(应用程序),处于用户态空间中,用户空间中的进程是不能直接操作底层的硬件的。
2.系统调用
区别于用户进程调用,系统调用是操作系统级别的api,比如java的读取数据过程(使用缓冲区),用户程序发起读操作,导致
“syscall read”系统调用,就会把数据搬到一个buffer中;用户程序发起写操作,导致“syscall write”系统调用,会把一个buffer中
的数据搬出去。
3.内核态
用户态的进程想要访问底层硬件,必须通过系统调用,由用户态切换到内核态。
4.局部性原理
操作系统在访问磁盘时,由于局部性原理,操作系统不会一次只读取一个字节(代价太大),而是会借助硬件直接存储器读取一片(一个或者多个磁盘块)数据。因此,就需要一个“中间缓冲区”–即内核缓冲区。先把数据从磁盘存到内核缓冲区,再把数据从内核缓冲区放入用户缓冲区。
-
从进程看:
进程分为用户态和内核态,用户进程属于用户态,系统进程属于内核态。 -
从内存看:
内存分为用户内存和内核内存,应用内存是用户内存,系统内存是内核内存。
操作系统与Java基于流的I/O模型有些不匹配。操作系统要移动的是大块数据(缓冲区),这往往是在的协助下完成的。
I/O类喜欢操作小块数据——单个字节、几行文本。结果,操作系统送来整缓冲区的数据,java.io的流数据类再花大量时间把它们拆成小块,
往往拷贝一个小块就要往返于几层对象。操作系统喜欢整卡车地运来数据,java.io类则喜欢一铲子一铲子地加工数据。
—— 引自《JAVA NIO》
java旧的IO方式,就是一个字节一个子字节地读取数据,这会导致进程在用户态和内核态之间频繁的切换状态,
系统开销大,效率极低。而基于缓冲的读写,避免了这种频繁的切换,提高了效率。
二、IO模型
一共有五种IO模型,java NIO使用的就是多路复用模型
-
阻塞IO:用户进程阻塞,知道IO就绪或者IO处理完成,阻塞才解除。
-
非阻塞IO:用户进程不阻塞,需要轮询IO是否就绪,很占CPU资源,所以一般不用。
-
多路复用IO:用户线程阻塞,需要轮询IO是否就绪,但是和非阻塞不一样的是轮询IO的进程是系统进程而不是用户进程,
实际上使用了代理,避免了CPU空转,所以效率高,而且一个进程可以管理多个socket。 -
信号驱动式IO:用户进程不阻塞,而是安装一个信号处理函数,当数据准备好时,进程会接收一个SIGIO信号,可以在
信号处理函数中调用IO操作函数处理数据。 -
异步IO:用户进程不阻塞,发起IO请求之后就做自己的事了,IO等待就绪和操作都交给操作系统去完成,完成后调用
用户进程去处理,所以整个过程都是异步的。