zerocopy与java知识相关小记

1 传统I/O

(整理参考: http://www.ibm.com/developerworks/library/j-zerocopy/
传统的I/O调用,读取文件内容再通过socket发送出去,代码如下:
File.read(fileDesc, buf, len);
Socket.send(socket, buf, len);
有很大的性能开销:
1. 上下文切换(context switch), 此处有4次用户态和内核态的切换
2. Buffer内存开销, 一个是应用程序buffer, 另一个是系统读取buffer以及socket buffer
其运行示意图如下:
这里写图片描述
1) 先将文件内容从磁盘中拷贝到操作系统buffer

2) 再从操作系统buffer拷贝到程序应用buffer

3) 从程序buffer拷贝到socket buffer

4) 从socket buffer拷贝到协议引擎

这里写图片描述
1) 调用read(), 程序切换到内核态
2) read()调用完毕, 返回数据, 程序切换回用户态
3) 调用send(), 程序切换到内核态
4) send()完毕, 程序切换回用户态

频繁的数据copy和上下文切换带来的内存消耗和cpu消耗导致了很大性能问题,我们仅仅转移数据不需要处理数据,所以此处应该是可以优化的,所以引入了zerocopy技术。

2 zerocopy

零拷贝避免了文件内容buffer的来回拷贝以及上下文的切换。
主要有两种实现方式:
① sendFile
利用DMA方式,减少CPU消耗,大块文件传输效率高,没有内存安全性问题。小文件传输效率低于mmap方式,只能使用非阻塞传输方式。
Java NIO中的 FileChannel.transferTo() 方法就是这样的实现, 这个实现是依赖于操作系统底层的sendFile()实现的。(虽然是来自NIO,但是这个传输是阻塞式的)
② mmap+write
小块文件传输效率高,不能很好的使用DMA,比第一种消耗更多CPU,有内存安全问题;
java中MappedByteBuffer相关类可以实现。阿里开源的rocketmq就是使用这种方式持久化数据。
这里写图片描述

这里写图片描述

Linux 2.4 及以后的内核, 又做了改进, 不再使用socket buffer, 而是直接将read buffer数据拷贝到协议引擎, 而socket buffer只会记录数据位置的描述符和数据长度:
这里写图片描述

补充知识:什么是上下文切换?
上下文切换(context switch)指cpu从一个进程或线程切换到另一个进行或线程。
上下文是指某一时间点 CPU 寄存器和程序计数器的内容。
这两个寄存器存储的就是程序运行所需要的相关的数据。
上下文切换可以认为是内核(操作系统的核心)在 CPU 上对于进程(包括线程)进行以下的活动:(1)挂起一个进程,将这个进程在 CPU 中的状态(上下文)存储于内存中的某处,(2)在内存中检索下一个进程的上下文并将其在 CPU 的寄存器中恢复,(3)跳转到程序计数器所指向的位置(即跳转到进程被中断时的代码行),以恢复该进程。

猜你喜欢

转载自blog.csdn.net/u011730199/article/details/78205058
今日推荐