将大数据写入套接字时最小化副本

问题描述 投票:9回答:2

我正在编写一个处理图像(大数据)的应用程序服务器。我正在尝试将图像数据发送回客户端时最小化副本。我需要发送给客户的处理过的图像位于从jemalloc获得的缓冲区中。我想到将数据发送回客户端的方法是:

1)简单的写呼叫。

// Allocate buffer buf.
// Store image data in this buffer.
write(socket, buf, len);

2)我通过mmap而不是jemalloc获取缓冲区,虽然我认为jemalloc已经使用mmap创建了缓冲区。然后我做一个简单的写电话。

buf = mmap(file, len);  // Imagine proper options.
// Store image data in this buffer.
write(socket, buf, len);

3)我像以前一样通过mmap获取缓冲区。然后我使用sendfile发送数据:

buf = mmap(in_fd, len);  // Imagine proper options.
// Store image data in this buffer.
int rc;
rc = sendfile(out_fd, file, &offset, count);
// Deal with rc.

似乎(1)和(2)可能会做同样的事情,因为jemalloc可能首先通过mmap分配内存。但我不确定(3)。这真的会带来任何好处吗?关于Linux零拷贝方法的article图4表明使用sendfile可以防止进一步复制:

没有数据被复制到套接字缓冲区。相反,只有具有关于数据的下落和长度信息的描述符被附加到套接字缓冲区。 DMA引擎将数据直接从内核缓冲区传递到协议引擎,从而消除了剩余的最终副本。

如果一切顺利,这似乎是一场胜利。我不知道我的mmaped缓冲区是否算作内核缓冲区。另外我不知道什么时候重新使用这个缓冲区是安全的。由于fd和length是唯一附加到套接字缓冲区的东西,我假设内核实际上异步地将这些数据写入套接字。如果它执行sendfile的返回表示什么?我怎么知道何时重用这个缓冲区?

所以我的问题是:

  1. 将大缓冲区(我的情况下的图像)写入套接字的最快方法是什么?图像保存在内存中。
  2. 在mmapped文件上调用sendfile是个好主意吗?如果是,那有什么问题?这甚至会带来任何胜利吗?
linux sockets networking sendfile
2个回答
4
投票

似乎我的怀疑是正确的。我从这个article得到了我的信息。引用它:

此外,这些网络写入系统调用(包括sendfile)可能并且在许多情况下确实在通过方法调用通过TCP发送的数据被确认之前返回。一旦将所有数据写入套接字缓冲区(sk buff)并将其推送到TCP写入队列,这些方法就会返回,TCP引擎可以从该点开始单独管理。换句话说,当sendfile返回时,最后一个TCP发送窗口实际上并没有发送到远程主机而是排队。在支持分散 - 聚集DMA的情况下,没有单独的缓冲区来保存这些字节,而缓冲区(sk buff)只保存指向文件内容所在的OS缓冲区高速缓存页面的指针。如果我们在返回sendfile后立即修改与最后一个TCP发送窗口中的数据相对应的文件的内容,则可能导致竞争条件。因此,TCP引擎可能会将新写入的数据发送到远程主机,而不是我们最初打算发送的数据。

如果来自mmapped文件的缓冲区甚至被认为是“DMA-able”,那么在没有来自实际客户端的显式确认(通过网络)的情况下,似乎无法知道何时重新使用它是安全的。我可能不得不坚持简单的写入调用并产生额外的副本。有更多细节的paper(也来自文章)。

编辑:拼接调用中的这个article也显示了问题。引用它:

请注意,在将数据从mmap的缓冲区拼接到网络套接字时,无法确定何时发送了所有数据。即使splice()返回,网络堆栈可能还没有发送所有数据。因此,重用缓冲区可能会覆盖未发送的数据。


1
投票

对于情况1和2 - 您标记为//在此缓冲区中存储图像数据的操作是否需要进行任何转换?它只是从内存到buf的简单副本吗?

如果它只是普通的副本,你可以直接使用从jemalloc获得的指针。

假设img是从jemalloc获得的指针,size是图像的大小,只需运行以下代码:

int result;
int sent=0;
while(sent<size) {
    result=write(socket,img+sent,size-sent);
    if(result<0) {
        /* error handling here */
        break;
    }
    sent+=result;
}

它可以正常阻止I / O(默认行为)。如果您需要以非阻塞方式编写数据,您应该能够自己重新编写代码,但现在您有了这个想法。

对于情况3 - sendfile用于将数据从一个描述符发送到另一个描述符。这意味着您可以,例如,将文件中的数据直接发送到tcp套接字,您不需要分配任何额外的缓冲区。因此,如果要发送到客户端的图像位于文件中,只需转发发送文件即可。如果你在内存中有它(因为你以某种方式处理它,或者只是生成它),请使用我前面提到的方法。

© www.soinside.com 2019 - 2024. All rights reserved.