关于Ceph中Bufferlist的设计与使用

云计算
如果非要在整个Ceph中,找出一个类最重要,我觉得非Bufferlist莫属了,原因很简单,因为Bufferlist负责管理Ceph中所有的内存。整个Ceph中所有涉及到内存的操作,无论是msg分配内存接收消息,还是OSD构造各类数据结构的持久化表示(encode/decode),再到实际磁盘操作,都将bufferlist作为基础。

[[134327]]

如果非要在整个Ceph中,找出一个类最重要,我觉得非Bufferlist莫属了,原因很简单,因为Bufferlist负责管理Ceph中所有的内存。整个Ceph中所有涉及到内存的操作,无论是msg分配内存接收消息,还是OSD构造各类数据结构的持久化表示(encode/decode),再到实际磁盘操作,都将bufferlist作为基础。

Ceph中bufferlist的设计还是有些复杂的,其中包含三个主要的内buffer::raw(bufferraw)、 buffer::ptr(bufferptr)和buffer::list(bufferlist)。这三个类都定义在common/buffer.h 中,都是buffer类的内部类,而buffer类本身没有任何内容,只起到了一个命名空间的作用。

这三个类的职责各有不同:

buffer::raw:对应一段真实的物理内存,负责维护这段物理内存的引用计数nref和释放操作。

buffer::ptr:对应Ceph中的一段被使用的内存,也就是某个bufferraw的一部分或者全部。

buffer::list:表示一个ptr的列表(std::list),相当于将N个ptr构成一个更大的虚拟的连续内存。

buffer这三个类的相互关系可以用下面这个图来表示:

 

Ceph中Bufferlist的设计与使用

图中蓝色的表示bufferlist,橙色表示bufferptr,绿色表示bufferraw。

在这个图中,实际占用的系统内存一共就三段,分别是raw0,raw1和raw2代表的三段内存。其中:

raw0被ptr0,ptr1,ptr2使用

raw1被ptr3,ptr4,ptr6使用

raw2被ptr5,ptr7使用

而list0是由ptr0-5组成的,list1是由ptr6和ptr7组成的。

从这张图上我们就可以看出bufferlist的设计思路了: 对于bufferlist来说,仅关心一个个ptr。bufferlist将ptr连在一起,当做是一段连续的内存使用。因此,可以通过 bufferlist::iterator一个字节一个字节的迭代整个bufferlist中的所有内容,而不需要关心到底有几个ptr,更不用关心这些 ptr到底和系统内存是怎么对应的;也可以通过bufferlist::write_file方法直接将bufferlist中的内容出到一个文件中;或者通过bufferlist::write_fd方法将bufferlist中的内容写入到某个fd中。

与bufferlist相对的是负责管理系统内存的bufferraw。bufferraw只关心一件事:维护其所管理的系统内存的引用计数,并且在引用计数减为0时——即没有ptr再使用这块内存时,释放这块内存。

连接bufferlist和bufferraw的是bufferptr。bufferptr关心的是如何使用内存。每一个bufferptr一定有一个bufferraw为其提供系统内存,然后ptr决定使用这块内存的哪一部分。bufferlist只用通过ptr才能对应到系统内存中,而 bufferptr而可以独立存在,只是大部分ptr还是为bufferlist服务的,独立的ptr使用的场景并不是很多。

通过引入ptr这样一个中间层次,bufferlist使用内存的方式可以非常灵活,这里可以举两个场景:

1. 快速encode/decode

在Ceph中经常需要将一个bufferlist编码(encode)到另一个bufferlist中,例如在msg发送消息的时候,通常msg拿到的 osd等逻辑层传递给它的bufferlist,然后msg还需要给这个bufferlist加上消息头和消息尾,而消息头和消息尾也是用 bufferlist表示的。这时候,msg通常会构造一个空的bufferlist,然后将消息头、消息尾、内容都encode到这个空的 bufferlist。而bufferlist之间的encode实际只需要做ptr的copy,而不涉及到系统内存的申请和Copy,效率较高。

2. 一次分配,多次使用

我们都知道,调用malloc之类的函数申请内存是非常重量级的操作。利用ptr这个中间层可以缓解这个问题,即我们可以一次性申请一块较大的内存,也就是一个较大的bufferraw,然后每次需要内存的时候,构造一个bufferptr,指向这个bufferraw的不同部分。这样就不再需要向系统申请内存了。***将这些ptr都加入到一个bufferlist中,就可以形成一个虚拟的连续内存。

关于作者:袁冬博士,UnitedStack产品副总裁,负责UnitedStack产品、售前和对外合作工作;云计算专家,在云计算、虚拟化、分布式系统和企业级应用等方面有丰富的经验;对分布式存储、非结构数据存储和存储虚拟化有深刻地理解,在云存储和企业级存储领域有丰富的研发与实践经验;Ceph等开源存储项目的核心代码贡献者。

原文链接:https://www.ustack.com/blog/bufferlist/

责任编辑:Ophira 来源: ustack博客
相关推荐

2015-05-20 10:05:10

Ceph分布式文件系统序列化

2023-02-28 08:00:24

2021-05-06 21:26:00

BcacheCeph存储

2015-03-24 20:10:21

2023-10-12 08:01:00

2018-04-12 08:37:27

2022-07-10 07:48:26

缓存软件设计

2009-12-31 10:13:23

宽带接入网

2017-12-06 14:35:01

OpenStackCeph存储

2010-03-26 19:22:08

F#代理

2018-05-22 08:37:02

Ceph存储存储系统

2009-12-28 16:55:25

2017-09-20 09:58:21

数据库“状态”字段设计

2011-09-02 19:12:59

IOS应用Sqlite数据库

2012-02-20 09:52:27

ibmdw

2011-06-13 10:21:25

QT 信号 槽机制

2010-01-14 09:35:10

WF4.0

2017-06-27 14:48:51

开发设计程序员

2020-07-19 10:26:47

Kubernetes数据结构

2019-11-07 23:48:12

shell脚本getopts
点赞
收藏

51CTO技术栈公众号