Raj*_*jiv 9 sockets linux networking sendfile
我正在编写一个处理图像(大数据)的应用程序服务器.我正在尝试将图像数据发送回客户端时最小化副本.我需要发送给客户的处理过的图像位于从jemalloc获得的缓冲区中.我想到将数据发送回客户端的方法是:
1)简单的写呼叫.
// Allocate buffer buf.
// Store image data in this buffer.
write(socket, buf, len);
Run Code Online (Sandbox Code Playgroud)
2)我通过mmap而不是jemalloc获取缓冲区,虽然我认为jemalloc已经使用mmap创建了缓冲区.然后我做一个简单的写电话.
buf = mmap(file, len); // Imagine proper options.
// Store image data in this buffer.
write(socket, buf, len);
Run Code Online (Sandbox Code Playgroud)
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.
Run Code Online (Sandbox Code Playgroud)
似乎(1)和(2)可能会做同样的事情,因为jemalloc可能首先通过mmap分配内存.但我不确定(3).这真的会带来任何好处吗?本文关于Linux零拷贝方法的图4 表明使用sendfile可以防止进一步的拷贝:
没有数据被复制到套接字缓冲区.相反,只有具有关于数据的下落和长度信息的描述符被附加到套接字缓冲区.DMA引擎将数据直接从内核缓冲区传递到协议引擎,从而消除了剩余的最终副本.
如果一切顺利,这似乎是一场胜利.我不知道我的mmaped缓冲区是否算作内核缓冲区.另外我不知道什么时候重新使用这个缓冲区是安全的.由于fd和length是唯一附加到套接字缓冲区的东西,我假设内核实际上异步地将这些数据写入套接字.如果它执行sendfile的返回表示什么?我怎么知道何时重用这个缓冲区?
所以我的问题是:
看来我的怀疑是正确的。我从这篇文章中得到了我的信息。引用其中:
此外,这些网络写入系统调用(包括 sendfile)可能并且在许多情况下确实会在方法调用通过 TCP 发送的数据被确认之前返回。一旦所有数据写入套接字缓冲区 (sk buff) 并推送到 TCP 写入队列,这些方法就会返回,从那时起 TCP 引擎就可以单独管理。换句话说,当 sendfile 返回时,最后一个 TCP 发送窗口实际上并未发送到远程主机,而是排队。在支持分散-聚集 DMA 的情况下,没有单独的缓冲区来保存这些字节,而是缓冲区(sk buffs)仅保存指向操作系统缓冲区高速缓存页面的指针,文件内容位于其中。如果我们在 sendfile 返回后立即修改与最后一个 TCP 发送窗口中的数据相对应的文件内容,则可能会导致竞争条件。因此,TCP 引擎可能会将新写入的数据发送到远程主机,而不是我们最初打算发送的数据。
假设映射文件中的缓冲区甚至被认为是“DMA-able”,似乎没有办法知道何时可以安全地重新使用它,而无需来自实际客户端的明确确认(通过网络)。我可能不得不坚持简单的写入调用并产生额外的副本。有一篇论文(也来自该文章)提供了更多详细信息。
编辑:这篇关于拼接调用的文章也显示了问题。引用它:
请注意,当将数据从 mmap 缓冲区拼接到网络套接字时,无法判断所有数据何时已发送。即使 splice() 返回,网络堆栈也可能尚未发送所有数据。因此重用缓冲区可能会覆盖未发送的数据。
| 归档时间: |
|
| 查看次数: |
4280 次 |
| 最近记录: |