如何在C ++中使用无锁循环缓冲区实现零拷贝tcp

时间:2012-07-02 14:16:05

标签: c++ tcp circular-buffer lockless zero-copy

我有多个线程需要使用TCP流中的数据。我希望在共享内存中使用循环缓冲区/队列来从TCP套接字读取。 TCP接收将直接写入循环队列。消费者将从队列中读取。

此设计应启用零拷贝和零锁定。但是这里有两个不同的问题。

  1. 从TCP套接字读取1条逻辑消息是否可能/有效?如果没有,并且我阅读了多条消息,我将不得不将残差复制到此 - >接下来。

  2. 是否真的可以实现无锁队列?我知道有原子操作,但这些也很昂贵。因为所有CPU缓存都需要无效。这将影响我所有24个内核的所有操作。

  3. 我在低级TCP中有点生疏,并不清楚如何判断消息何时完成。我是在寻找\ 0还是具体实现?

    TY

2 个答案:

答案 0 :(得分:8)

不幸的是,TCP无法传输消息,只能传输字节流。如果要传输邮件,则必须在顶部应用协议。高性能的最佳协议是使用指定消息长度的健全性检查标头的协议 - 这允许您将正确的数据量直接读取到合适的缓冲区对象中,而无需逐字节地迭代数据以寻找结束 - 消息字符。然后,缓冲区POINTER可以排队到另一个线程,并为下一条消息创建/解释新的缓冲区对象。这避免了批量数据的任何复制,并且对于大型消息,足够有效,因为对消息对象指针使用非阻塞队列有点没有意义。

下一个优化avaialble是集合对象*缓冲区以避免持续的新/处置,回收消费者线程中的*缓冲区以便在网络接收线程中重用。这对于ConcurrentQueue来说相当容易,最好是阻塞以允许流控制而不是数据损坏或段错误/ AV如果池暂时清空。

接下来,在每个*缓冲区数据成员的开头添加一个[cacheline size]'dead-zone',以防止任何线程与任何其他线程共享数据。

结果应该是一个高带宽的完整消息流进入消费者线程,只有很少的延迟,CPU浪费或缓存抖动。所有24个内核都可以在不同的数据上运行平稳。

在多线程应用程序中复制批量数据是对设计和失败的承认。

跟进..

由于协议不同,听起来你因为迭代数据而陷入困境:(

无假共享的PDU缓冲区对象,例如:

typedef struct{
  char deadZone[256];  // anti-false-sharing
  int dataLen;
  char data[8388608]; // 8 meg of data
} SbufferData;

class TdataBuffer: public{
private:
  TbufferPool *myPool; // reference to pool used, in case more than one
  EpduState PDUstate; // enum state variable used to decode protocol
protected:
  SbufferData netData;
public:
  virtual reInit(); // zeros dataLen, resets PDUstate etc. - call when depooling a buffer
  virtual int loadPDU(char *fromHere,int len);  // loads protocol unit
  release(); // pushes 'this' back onto 'myPool'
};

loadPDU传递一个指向原始网络数据长度的指针。它返回0 - 意味着它还没有完全组装PDU,或者它从原始网络数据中完全组装PDU的字节数,在这种情况下,将其排队,取消另一个并调用loadPDU()使用未使用的原始数据,然后继续使用下一个原始数据。

如果需要,您可以使用不同派生缓冲区类的不同池来提供不同的协议 - 一组TbufferPool [Eprotocols]。 TbufferPool可能只是一个BlockingCollection队列。管理几乎变得微不足道 - 缓冲区可以在整个系统的队列中发送到GUI以显示统计信息,然后可能发送到记录器,只要在队列链的末尾调用release()。 / p>

显然,'真正的'PDU对象可以加载更多方法,数据联合/结构,迭代器可能以及操作协议的状态引擎,但这无论如何都是基本的想法。最重要的是易于管理,封装,因为没有两个线程可以在同一个缓冲区实例上运行,所以不需要锁/同步来解析/访问数据。

哦,是的,并且由于没有队列保持锁定的时间超过推送/弹出一个指针所需的时间,实际争用的可能性非常低 - 即使是传统的阻塞队列也几乎不需要使用内核锁定。

答案 1 :(得分:0)

如果您使用的是Windows 8或Windows Server 2012,则可以使用已注册的I / O,它可为较低的CPU提供比常规IOCP更高的带宽;它通过切断内核转换,零拷贝等来实现这一点

API: http://msdn.microsoft.com/en-us/library/windows/desktop/ms740642%28v=vs.85%29.aspx

背景资料: http://www.serverframework.com/asynchronousevents/rio/