预备知识关于I/O内存映射。 设备通过控制总线,数据总线,状态总线与CPU相连。控制总数传送控制信号。在传统的操作中,都是通过读写设备寄存器的值来实现。但是这样耗费了CPU时钟。而且每取一次值都要读取设备寄存器,造成了效率的低下。在现代操作系统中。引用了I/O内存映射。即把寄存器的值映身到主存。对设备寄存器的操作,转换为对主存的操作,这样极大的提高了效率。 关于DMA传统的处理方法为:当设备接收到数据,向CPU报告中断。CPU处理中断,CPU把数据从设备的寄存器数据读到内存。 在现代操作系统中引入的DMA设备,设备接收到数据时,把数据放至DMA内存,再向CPU产生中断。这样节省了大量的CPU时间 什么是零拷贝? 零拷贝描述的是CPU不执行拷贝数据从一个存储区域到另一个存储区域的任务,这通常用于通过网络传输一个文件时以减少CPU周期和内存带宽。 避免数据拷贝
零拷贝给我们带来的好处
零拷贝完全依赖于操作系统。操作系统支持 通过sendfile实现的零拷贝I/Osendfile(socket, file, len);//file可以是文件句柄,也可以是socket句柄 把文件数据通过网络发送出去,减少了上下文的切换,内核的缓存数据到直接网卡数据也不用CPU去复制,由DMA完成
通过sendfile实现的零拷贝I/O只使用了2次用户空间与内核空间的上下文切换,以及3次数据的拷贝。实现了把数据从文件发送到网卡。 传统的IO流程实现文件数据发送read(file, tmp_buf, len);write(socket, tmp_buf, len);
第4步DMP把socket缓存数据复制到网卡缓存上 经过4次上下文切换,4次数据拷贝,在数据量比较大时,性能比sendfile方式低下 通过mmap实现的零拷贝I/Ommap(内存映射)是一个比sendfile昂贵但优于传统I/O的方法。 MappedByteBuffer 在调用FileChannel.map()时使用。 与DirectByteBuffer类似,这也是JVM堆外部的情况。 它基本上作为OS mmap()系统调用的包装函数,以便代码直接操作映射的物理内存数据。 HeapByteBuffer 在调用ByteBuffer.allocate()时使用。 它被称为堆,因为它保存在JVM的堆空间中,因此你可以获得所有优势,如GC支持和缓存优化。 但是,它不是页面对齐的,这意味着如果你需要通过JNI与本地代码交互时,比如写入网卡,写入磁盘,JVM将不得不复制到系统的页缓冲区空间。 DirectByteBuffer 在调用ByteBuffer.allocateDirect()时使用。 JVM在堆空间之外分配内存空间。 因为它不是由JVM管理的,所以你的内存空间是页面对齐的,不受GC影响,这使得它成为处理本地代码的完美选择。 然而,你要C程序员一样,自己管理这个内存,必须自己分配和释放内存来防止内存泄漏。 splice 在两个文件描述符之间传输数据,不用拷贝。但输入和输出文件描述符必须有一个是pipe。也就是说如果你需要从一个socket 传输数据到另外一个socket,是需要使用 pipe来做为中介的 splice (socket1_fd, pipe_fd) splice (pipl_fd, socket2_fd) |
|