简介
在学习kafka如何实现高吞吐量的时候遇到一个技术zero copy,追随kafka文档中的链接深入了解了一下zero copy,在这里mark一下,原文链接https://www.ibm.com/developerworks/linux/library/j-zerocopy/
通常的实现方式
假设这样一个场景:从文件中读取数据然后通过网络将数据发送出去,实现的核心部分一般是这样的
File.read(fileDesc, buf, len);
Socket.send(socket, buf, len);
尽管实现非常简单,需要4次用户态和内核态的上线文切换,数据需要拷贝4次,整个流程如下图
Paste_Image.png- read()从用户态切换到内核态,调用sys_read()从文件中读取数据,数据从disk拷贝到内核地址空间
- 数据从内核地址空间拷贝到用户地址空间,read函数返回
- send函数从用户态切换到内核态,将数据从用户地址空间拷贝到内核地址空间
- 数据从内核地址空间拷贝到协议引擎,send函数返回
使用内核对数据缓存相比于用户直接读取数据好像是低效的,在某些情况下内核态的数据缓存确实会提高效率,但是当数据大小超过内核缓存的大小时,这个操作是低效的。
zero copy
其实对于这种场景下,数据没有必要从内核态拷贝到用户态,可以直接从read buffer拷贝到socket buffer,transferTo()函数就是实现这样的操作,但是transferTo函数需要操作系统的支持,在linux下sendfile实现了这样的操作,刚才的实现可以改成下面的:
transferTo(position, count, writableChannel);
这样数据就不需要从内核空间拷贝到用户空间了
Paste_Image.png如果内核支持gather operations的话还可以进一步优化,在用户实现层面没有发生变化,优化都是在内核实现中的,首先数据从disk拷贝到内核buffer,然后并不需要把数据再次拷贝到socket buffer,只需要把数据位置和长度告诉socket buffer即可,优化之后的数据流如下:
Paste_Image.png
网友评论