vmsplice()和TCP

在原来的vmsplice()实现中, 有人build议 ,如果你的用户空间缓冲区是可以放在一个pipe道中的最大页面数的2倍,那么在缓冲区的后半部分成功的vmsplice()可以保证内核是使用缓冲区的前半部分完成的。

但毕竟这并不是真的,特别是对于TCP,内核页面将被保留直到从对方收到ACK。 解决这个问题留作未来工作,因此对于TCP,内核仍然需要从pipe道复制页面。

vmsplice()SPLICE_F_GIFT选项,可以解决这个问题,但问题是这暴露了另外两个问题 – 如何高效地从内核获取新的页面,以及如何减lesscaching垃圾。 第一个问题是mmap要求内核清除页面,第二个问题是尽pipemmap可能会在内核中使用花哨的kscrubd特性,这会增加进程的工作集(caching垃圾)。

基于此,我有这些问题:

  • 通知用户有关网页安全重复使用的现状是什么? 我特别感兴趣的页面splice()d到套接字(TCP)。 过去5年有什么事情发生?
  • mmap / vmsplice / splice / munmap当前在TCP服务器中进行零拷贝的最佳实践,还是今天我们有更好的select?

是的,由于TCP套接字在页面上保持不确定的时间,所以不能使用示例代码中提到的双缓冲方案。 另外,在我的用例中,页面来自循环缓冲区,所以我不能将页面送到内核并分配新的页面。 我可以验证收到的数据中看到数据损坏。

我采取了轮询TCP套接字的发送队列的级别,直到它耗尽到0.这修复了数据损坏,但是不理想,因为将发送队列排空到0会影响吞吐量。

 n = ::vmsplice(mVmsplicePipe.fd.w, &iov, 1, 0); while (n) { // splice pipe to socket m = ::splice(mVmsplicePipe.fd.r, NULL, mFd, NULL, n, 0); n -= m; } while(1) { int outsize=0; int result; usleep(20000); result = ::ioctl(mFd, SIOCOUTQ, &outsize); if (result == 0) { LOG_NOISE("outsize %d", outsize); } else { LOG_ERR_PERROR("SIOCOUTQ"); break; } //if (outsize <= (bufLen >> 1)) { if (outsize == 0) { LOG("outsize %d <= %u", outsize, bufLen>>1); break; } };