【muduo】关于自动增长的缓冲区


写详细比较费时间,就简单总结下。

总结自Linux 多线程服务端编程:使用 muduo C++ 网络库

Muduo网络编程:
IO-multiplex+non-blocking

为什么需要缓冲区

Non-blocking IO 的核心思想是避免阻塞在 read() 或 write() 或其他 IO 系统调
用上,所以应用层的缓冲是必须的,每个 TCP socket 都要有 input buffer
和 output buffer。

自动增长的缓冲区

一方面希望减少系统调用,所以缓冲区越大越好
另一方面希望减少内存占用,如果缓冲区很大而连接很多,将会占用大量内存空间。而大多数时候缓冲区的使用率又很低。

buffer数据结构

在这里插入图片描述

buffer类

Muduo Buffer的size()是自适应的,一开始的初始值是1k。

抽取相关代码:

class Buffer:public muduo::copyable{
 public:
  static const size_t kCheapPrepend = 8;
  static const size_t kInitialSize = 1024;

  explicit Buffer(size_t initialSize = kInitialSize)
    : buffer_(kCheapPrepend + initialSize),
      readerIndex_(kCheapPrepend),
      writerIndex_(kCheapPrepend){...}
      ...
      
      std::vector<char> buffer_;
}

vector数组,保证了可以扩展,初始化了1k可用空间,并在头部预留了1Byte。

具体做法是,在栈上准备一个 65536 字节的 extrabuf,然后利用 readv() 来读取数据。
iovec 有两块,第一块指向muduo Buffer 中的 writable 字节,另一块指向 栈上的 stackbuf。
这样如果读入的数据不多,那么全部都读到Buffer 中去了;如果长度超过 Buffer 的 writable 字节数,就会读到栈上的 extrabuf 里,然后程序再把extrabuf 里的数据 append 到 Buffer 中。

这么做利用了临时栈上空间,避免开巨大 Buffer 造成的内存浪费,也避免反复 调用 read() 的系统开销(通常一次 readv()系统调用就能读完全部数据)。

代码相关实现:Buffer::readFd()

ssize_t Buffer::readFd(int fd, int* savedErrno)
{
  // saved an ioctl()/FIONREAD call to tell how much to read
  char extrabuf[65536];
  struct iovec vec[2];
  const size_t writable = writableBytes();
  vec[0].iov_base = begin()+writerIndex_;
  vec[0].iov_len = writable;
  vec[1].iov_base = extrabuf;
  vec[1].iov_len = sizeof extrabuf;
  // when there is enough space in this buffer, don't read into extrabuf.
  // when extrabuf is used, we read 128k-1 bytes at most.
  const int iovcnt = (writable < sizeof extrabuf) ? 2 : 1;
  const ssize_t n = sockets::readv(fd, vec, iovcnt);
  if (n < 0)
  {
    *savedErrno = errno;
  }
  else if (implicit_cast<size_t>(n) <= writable)
  {
    writerIndex_ += n;
  }
  else
  {
    writerIndex_ = buffer_.size();
    append(extrabuf, n - writable);
  }
  return n;
}

首先写buffer,写不下的写extrabuf,然后把extrabuf的写入buffer,具体函数是append。

  void append(const char* /*restrict*/ data, size_t len)
  {
    ensureWritableBytes(len);
    std::copy(data, data+len, beginWrite());
    hasWritten(len);
  }

ensureWritableBytes检查buffer是否有空余空间可以被写入,没有就vector.resize(len),扩展len。然后通过std::copy,把extrabuf上的内存copy到buffer上。最后更新writerIndex_。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值