"零拷贝网络"和"内核旁路"有什么区别?它们是两个含义相同或不同的短语吗?内核绕过"零复制网络"中使用的技术,这是关系吗?
linux networking operating-system network-programming zero-copy
当引入splice时,在内核列表中讨论了sendfile是基于splice重新实现的.splice SLICE_F_MOVE的文档说明:
尝试移动页面而不是复制.这只是内核的提示:如果内核无法从管道中移动页面,或者管道缓冲区没有引用整页,则仍可能会复制页面.这个标志的初始实现是错误的:因此从Linux 2.6.21开始它是一个无操作(但仍然允许在splice()调用中); 将来,可以恢复正确的实施.
这是否意味着Linux没有写入套接字的零拷贝方法?或者这是在某些时候修复的,多年来没有人更新文档?sendfile或splice中的任何一个在任何最新的3.x内核版本中都有零拷贝实现吗?
由于谷歌没有回答这个问题,我正在为下一个可怜的骗子创建一个stackoverflow问题,他想知道使用vmsplice和splice或sendfile是否有利于普通旧写.
在最初的vmsplice()实现中,有人建议如果你有一个用户区缓冲区2x可以容纳管道的最大页数,那么缓冲区后半部分成功的vmsplice()将保证内核完成使用缓冲区的前半部分.
但事实并非如此,特别是对于TCP,内核页面将被保留,直到从另一方接收到ACK.修复这个问题是未来的工作,因此对于TCP,内核仍然需要从管道中复制页面.
vmsplice()有一个SPLICE_F_GIFT选项可以处理这个,但问题是这暴露了另外两个问题 - 如何有效地从内核获取新页面,以及如何减少缓存垃圾.第一个问题是mmap需要内核清除页面,第二个问题是虽然mmap可能会使用内核中的花式kscrubd功能,但这会增加进程的工作集(缓存垃圾).
基于此,我有以下问题:
mmap/ vmsplice/ splice/ munmap目前在TCP服务器中进行零复制的最佳实践还是今天我们有更好的选择?基本上,如果一个人有一个用于空终止字符串的预加载缓冲区和要引用的长度,并且想要将对它的引用传递到一个采用 std::string & 但不复制该字符串或拥有它的方法中,是吗?可能这样做吗?
这只具有有限的寿命,并且以仅在缓冲区有效时才有效的方式进行管理。
我正在Cyclone V SoC上运行Linux 5.1,这是一个FPGA,在一个芯片中具有两个ARMv7内核。我的目标是从外部接口收集大量数据,并通过TCP套接字流出(部分)这些数据。这里的挑战是数据速率非常高,并且可能接近饱和GbE接口。我有一个write()可行的实现,该实现只使用对套接字的调用,但其最高速度为55MB / s;大约是理论GbE限制的一半。我现在正在尝试使零拷贝TCP传输能够提高吞吐量,但是我遇到了麻烦。
为了将数据从FPGA传送到Linux用户空间,我编写了一个内核驱动程序。该驱动程序使用FPGA中的DMA模块将大量数据从外部接口复制到连接到ARMv7内核的DDR3存储器中。当使用dma_alloc_coherent()进行探测时GFP_USER,驱动程序将此内存分配为一堆连续的1MB缓冲区,并通过mmap()在文件中实现并将这些/dev/地址返回给应用程序使用dma_mmap_coherent()预分配的缓冲区,将这些缓冲区公开给用户空间应用程序。
到目前为止,一切都很好; 用户空间应用程序正在查看有效数据,并且吞吐量超过360MB / s足够多,并有剩余空间(外部接口的速度不足以真正看到上限)。
为了实现零拷贝TCP网络,我的第一种方法是SO_ZEROCOPY在套接字上使用:
sent_bytes = send(fd, buf, len, MSG_ZEROCOPY);
if (sent_bytes < 0) {
perror("send");
return -1;
}
Run Code Online (Sandbox Code Playgroud)
但是,这导致send: Bad address。
谷歌搜索了一段时间之后,我的第二种方法是使用管道,splice()然后执行以下操作vmsplice():
ssize_t sent_bytes;
int pipes[2];
struct iovec iov = {
.iov_base = buf,
.iov_len = len
};
pipe(pipes);
sent_bytes = vmsplice(pipes[1], &iov, 1, 0);
if (sent_bytes < 0) {
perror("vmsplice"); …Run Code Online (Sandbox Code Playgroud) 我有两个打开的文件对象,dest和src.dest打开文件对象进行写入,将搜索位置放在文件中的某个偏移处,src打开文件对象进行读取.我需要做的只是从当前位置读取src到EOF并dest尽可能快地传输内容.
如果我使用Java编程,我可以利用该FileChannel#transferTo()方法执行零拷贝文件I/O.
Python也支持零拷贝吗?
我正在使用tee()复制一个"master"管道,使用splice()写入多个套接字.当然,这些管道将以不同的速率清空,具体取决于我可以拼接()到目标套接字的程度.因此,当我接下来将数据添加到"主"管道然后再次发送()时,我可能会遇到这样的情况,即我可以将64KB写入管道,但只有4KB到"从"管道之一.我猜测如果我将所有"主"管道拼接到套接字,我将永远无法将剩余的60KB传送给该管道.真的吗?我想我可以跟踪一个tee_offset(从0开始),我将其设置为"unteed"数据的开头,然后不要拼接()过去.因此,在这种情况下,我会将tee_offset设置为4096并且不会拼接更多,直到我能够将其发送给所有其他管道.我在这里走在正确的轨道上吗?有什么提示/警告吗?
有没有人知道在两个或多个套接字之间执行零拷贝数据传输的任何好的java库/ API包?我知道Java的NIO API可以分别使用java.nio.channels.FileChannel.transferTo和java.nio.channels.FileChannel.transferFrom方法从磁盘到套接字执行零拷贝数据传输,反之亦然.但是,似乎没有支持Java套接字到套接字零拷贝传输.此外,任何可以执行系统调用拼接的java库/ API(可以将数据从文件描述符传输到管道,反之亦然)将是一个优势,最好是在Linux平台上.
谢谢你的回复.
此外,我已经阅读了大多数以前关于零拷贝的博客以及其他信息性网站,例如http://www.ibm.com/developerworks/library/j-zerocopy/ ; 但是,似乎上述问题尚未得到解决.
我想知道为什么linux内核(或任何其他主流操作系统)没有零拷贝网络功能的原因?零拷贝我的意思是,数据包/数据流不会被复制以传递给用户空间中的应用程序,但是例如使用内存池类型的分配器来共享内核和用户空间之间的内存.我自己想出了3个理论:
a)我猜有安全问题.但当它们仅用作缓冲区时,是否真的没有办法在用户空间和内核之间安全地共享内存?
b)我猜有稳定性问题.但我们不能假设谁使用零拷贝网络,例如需要实例化并为内核调用传递内存池,是否知道内存管理?意识到足以避免泄漏?
c)到目前为止还没有完成/需要.我无法想象没有人要求这个功能,因为每个使用小数据包大小的人通常会受到"慢速"TCP堆栈实施的瓶颈,并且有第三方工具提供用于0-copy网络以供特殊使用网卡.
随意发布任何猜测,但请注明您是否正在假设或更深入了解保持StackOverflow质量的原因:-)