您的位置:首页 > 教育 > 锐评 > 三亚网站制作_杭州市人民政府信息公开网_特大新闻凌晨刚刚发生_爱站网关键词搜索

三亚网站制作_杭州市人民政府信息公开网_特大新闻凌晨刚刚发生_爱站网关键词搜索

2025/4/10 10:40:21 来源:https://blog.csdn.net/2404_87273268/article/details/144156907  浏览:    关键词:三亚网站制作_杭州市人民政府信息公开网_特大新闻凌晨刚刚发生_爱站网关键词搜索
三亚网站制作_杭州市人民政府信息公开网_特大新闻凌晨刚刚发生_爱站网关键词搜索

剖析Muduo Buffer

本文部分内容摘自陈硕的Blog – Muduo设计与实现之一:Buffer类的设计,并附上自己的理解,如有不对,请指出🤩

Buffer的要求

Muduo Buffer的设计要点:

  • 对外表现为一块连续的内存(char*, len)
  • 是一个动态数组,大小可以自动增长适应不同大小的消息
  • 内部以vector of char来保存数据,并提供相应的接口

按照陈硕的话来讲,Muduo Buffer 就像一个queue,从末尾写入数据,从头部读出数据。

Buffer的数据结构

Buffer 的内部是一个 vector of char,它是一块连续的内存。此外,Buffer 有两个 data

members,指向该 vector 中的元素。这两个 indices 的类型是 int,不是 char*,目的是应对

迭代器失效。muduo Buffer 的设计参考了 Netty 的 ChannelBuffer 和 libevent 1.4.x 的

evbuffer。

Muduo Buffer 的数据结构如下:

image-20241130162034362

两个 indices 把 vector 的内容分为三块:prependable、readable(可读区域)、writable(可写区域),各块的大小是(公式一):

prependable = readIndex

readable = writeIndex - readIndex

writable = size() - writeIndex

readIndex 和 writeIndex 满足以下不变式(invariant):

0 ≤ readIndex ≤ writeIndex ≤ data.size()

Muduo Buffer 里有两个常数 kCheapPrepend 和 kInitialSize,定义了 prependable 的初始大小和 writable 的初始大小。(readable 的初始大小为 0。)在初始化之后,Buffer 的数据结构如下:括号里的数字是该变量或常量的值。

image-20241130162241316

根据以上(公式一)可算出各块的大小,刚刚初始化的 Buffer 里没有 payload 数据,所以 readable == 0。

以下是muduo::Buffer的类图,图片来源于陈硕的博客

image-20241130154722794

具体的操作可以去看以下陈硕的博客,写的很清楚。

Buffer::readFd()

陈硕原话:

  • 在非阻塞网络编程中,如何设计并使用缓冲区?一方面我们希望减少系统调用,一次读的数据越多越划算,那么似乎应该准备一个大的缓冲区。另一方面,我们系统减少内存占用。如果有 10k 个连接,每个连接一建立就分配 64k 的读缓冲的话,将占用 640M 内存,而大多数时候这些缓冲区的使用率很低。muduo 用 readv 结合栈上空间巧妙地解决了这个问题。

系统调用readvUNIX 网络编程306页有讲解,具体作用为:运行一个系统调用读取多个缓冲区。并把这种操作称为分散读

在muduo中如何运用readv

  1. 在栈上准备65536字节的extrabuf
  2. 比较Buffer可写字节数writableextrabuf的大小
  3. 若Buffer可写字节数writable小于extrabuf,那么用这两块缓冲区作为分散读的操作对象
  4. 反之,只用writable作为分散读的操作对象(其实就是若writable>65535字节,就选择一个缓冲区;writable<= 65535字节,就选择他们两个缓冲区)
  5. 这样如果读入的数据不多,那么全部都读到 Buffer 中去了;如果长度超过 Buffer 的 writable 字节数,就会读到栈上的 extrabuf里,然后程序再把 extrabuf里的数据 append 到 Buffer 中。

这么做的好处:

  • 利用栈上的空间做临时空间,避免开辟巨大的 Buffer 造成浪费
  • 避免反复调用 read 系统调用

大致代码如下:

ssize_t Buffer::readFd(int fd, int *saveErrno)
{	char extrabuf[65536] = {0};struct iovec vec[2];     const std::size_t writable = writableBytes();// Buffer底层缓冲区可写的空间大小vec[0].iov_base = begin() + writerIndex_; vec[0].iov_len = writable;vec[1].iov_base = extrabuf;vec[1].iov_len = sizeof extrabuf;const int iovcnt = (writable < sizeof extrabuf) ? 2 : 1;const ssize_t n = readv(fd, vec, iovcnt);if(n < 0){*savenoErrno = errno;}else if(n <= writable){writerIndex_ += n;}else{writerIndex_ = buffer_.size();append(extrabuf, n - writable);}return n;
}writerIndex_ = buffer_.size();append(extrabuf, n - writable);}return n;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com