重回东软了,据说可能要做一个跟文件相关的项目,于是决定把Java NIO的内容再捡起来,看看。
为什么要使用NIO,其实在低连接数的情况下,NIO的性能是要低于IO的;但是在高并发的情况下,确实NIO性能好得多;因为IO是会为为每一个连接都分配一个线程/进程进行处理,NIO则只是使用一个线程/进程,通过系统地select机制进行轮询获取请求信息;
默认情况下,设备控制器不能实现DMA(directional Memory Access),去操作user space,但是经过虚拟内存,已经可以实现了;虚拟内存就是将内核空间地址到物理地址的映射(map),供user space使用;
IO的基本概念就是buffer的处理,所谓的input/outputqish1其实就是数据从buffer中放入移出;
进程使用的是user space,发送一个read命令到内核,通过buffer(内存空间)将命令以及命令数据发送到kenal space,kenal space将命令进行处理,从硬盘中去读数据,硬盘通过DMA(不需要经过CPU)直接将数据写入到kernal memory,kernal space再将数据进行重新组装(因为从设备过来的数据data-block,需要kernal space进行处理拆包打包后封装成byte buffer),并拷贝到临时的buffer中发送到user space中;
在现代操作系统中引入了虚拟内存(Virtual Memory)的概念,VM的好处两点:多个虚拟地址空间可以映射到同一个物理内存(比如user space以及kernal space都可以访问物理的内存空间);虚拟空间可以大于物理内存空间(超出的可以放置到硬盘的交换区);这样,作为内存,他的定位就是(沦为)物理硬盘的一个缓存,根据需要的内容置换到内存中去;
有了虚拟内存之后,上面介绍的操作中"并拷贝到临时的buffer中发送到user space中"的步骤就可以省略了,因为虚拟地址可以让user space和kernal space共享相同的物理内存地址,只要他们都是访问虚拟地址;userspace也可以访问到kernalspace组装好的bytebuffer;但是因为这样意味着user space可以直接读取设备(比如硬盘)设置的内存空间,所以这意味着需要对于内存进行分页(物理内存以及虚拟内存,一般两者分页大小一样);通常情况下,每页的大小是block的倍数,每个物理扇片的大小是521btye,内存分页大小为1024,2048以及4096;
在CPU和内存之间其实还有一个组件,称之为MMU,Memory Management Unit,专门用于将虚拟地址转换定位到物理地址,他负责定位对应"页";当需要的页并不在当前内存中,就会触发一个fault page,如果请求的虚拟地址是在物理硬盘正存放的,那么需要把所需的页从硬盘中取出来放入到内存中;如果请求的地址并不存在,那么,这个进程将会被杀死(kill);在这个过程中,可能是需要进行置换(stole)一部分当前内存的页到物理硬盘中,这个时候就需要对这些页进行复制然后传入到硬盘中;
其实所谓的"虚拟空间可以大于物理内存空间"是指虚拟空间他自己另外造了一套体系,实现虚拟空间和内存,虚拟空间和硬盘通过页的映射;所以已经不再仅仅局限于物理内存的概念了;物理内存空间的角色更像是虚拟空间的一个物理缓存;
并不是所有的设备都是采用block方式进行传输,比如CD机,打印机端口,网络端口等设备就是采用流(stream)的方式;stream的方式其实速度是比block方式要慢,一般用于间歇性通信情况;对于stream的接受有两种方式,一种是nonblock方式,这种方式是让进程去监听是否有可用的流准备好了(没有,进程可以释放做别的事情,有,进程则专职处理流);还有一种方式就是readiness selection,操作系统将会被通知,告知有流准备好了,前者是进程主动监控是否有可用流;后者则是通知机制(草莓园与园丁模式);
MappedByteBuffer使用:
FileInputStream fis = new FileInputStream(sourcePath);
FileOutputStream fos = new FileOutputStream(targetPath);
FileChannel fc = fis.getChannel();
FileChannel out = fos.getChannel();
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
out.write(mbb);
out.close();
这里需要注意,mbb不需要flip,因为fa.map返回的就是个完美的ByteBuffer;position就是0,limit就是本来长度;如果你使用flip之后,将会导致limit=position=0;到最后ByteBuffer的塌陷;