本节书摘来自华章出版社《Ceph源码分析》一书中的第2章,第2.2节Buffer,作者常涛,更多章节内容可以访问云栖社区“华章计算机”公众号查看
2.2 Buffer
Buffer就是一个命名空间,在这个命名空间下定义了Buffer相关的数据结构, 这些数据结构在Ceph的源代码中广泛使用。下面介绍的buffer::raw类是基础类,其子类完成了Buffer数据空间的分配,buffer::ptr类实现了Buffer内部的一段数据,buffer::list封装了多个数据段。
2.2.1 buffer::raw
类buffer::raw是一个原始的数据Buffer,在其基础之上添加了长度、引用计数和额外的crc校验信息,结构如下:
`class buffer::raw {
public:
char *data; //数据指针
unsigned len; //数据长度
atomic_t nref; //引用计数`mutable RWLock crc_lock; //读写锁,保护crc_map
map<pair<size_t, size_t>, pair<uint32_t, uint32_t> > crc_map;
//crc校验信息,第一个pair为数据段的起始和结束(from,to),第二个pair是crc32校验码,pair的第一字段为base crc32校验码,第二个字段为加上数据段后计算出的crc32校验码。
……
}
下列类都继承了buffer::raw,实现了data对应内存空间的申请:
类raw_malloc实现了用malloc函数分配内存空间的功能。
类class buffer::raw_mmap_pages实现了通过mmap来把内存匿名映射到进程的地址空间。
类class buffer::raw_posix_aligned调用了函数posix_memalign来申请内存地址对齐的内存空间。
类class buffer::raw_hack_aligned是在系统不支持内存对齐申请的情况下自己实现了内存地址的对齐。
类class buffer::raw_pipe实现了pipe做为Buffer的内存空间。
类class buffer::raw_char使用了C++的new操作符来申请内存空间。
2.2.2 buffer::ptr
类buffer::ptr就是对于buffer::raw的一个部分数据段。结构如下:
`class CEPH_BUFFER_API ptr {
raw *_raw;
unsigned _off, _len;
……
}`
ptr是raw里的一个任意的数据段,_off是在_raw里的偏移量,_len是ptr的长度。raw和ptr的示意图如图2-1所示。
图2-1 raw和ptr示意图
2.2.3 buffer::list
类buffer::list是一个使用广泛的类,它是多个buffer::ptr的列表,也就是多个内存数据段的列表。结构如下:
`class CEPH_BUFFER_API list {
std::list _buffers; //所有的ptr
unsigned _len; //所有的ptr的数据总长度
unsigned _memcopy_count; //当调用函数rebuild用来内存对齐时,需要内存拷贝的数据量
ptr append_buffer; //当有小的数据就添加到这个buffer里
mutable iterator last_p; //访问list的迭代器
……
}`
buffer::list的重要的操作如下所示。
添加一个ptr到list的头部:
`void push_front(ptr& bp) {
if (bp.length() == 0)
return;
_buffers.push_front(bp);
_len += bp.length();
}`
添加一个raw到list头部中,先构造一个ptr,后添加list中:
`void push_front(raw *r) {
ptr bp(r);
push_front(bp);
}`
判断内存是否以参数align对齐,每一个ptr都必须以align对齐:
`bool buffer::list::is_aligned(unsigned align) const
{
for (std::list::const_iterator it = _buffers.begin();
it != _buffers.end();
++it) if (!it->is_aligned(align))
return false;
return true;
}`
添加一个字符到list中,先查看append_buffer是否有足够的空间,如果没有,就新申请一个4KB大小的空间:
``void buffer::list::append(char c)
{
// 检查当前的append_buffer是否有足够的空间
unsigned gap = append_buffer.unused_tail_length();
if (!gap) {
// 如果没有空间,就申请一个append_buffer!
append_buffer = create_aligned(CEPH_BUFFER_APPEND_SIZE,
CEPH_BUFFER_APPEND_SIZE);
append_buffer.set_length(0); //到目前为止,没有用到
}`
append(append_buffer, append_buffer.append(c) - 1, 1);
// 把该数据段添加到append_buffer中
}``
内存对齐:有些情况下,需要内存地址对齐,例如当以directIO方式写入数据至磁盘时,需要内存地址按内存页面大小(page)对齐,也即buffer::list的内存地址都需按page对齐。函数rebuild用来完成对齐的功能。其实现的方法也比较简单,检查没有对齐的ptr,申请一块新对齐的内存,把数据拷贝过去,释放内存空间就可以了。
buffer::list还集成了其他额外的一些功能:
把数据写入文件或从文件读取数据的功能。
计算数据的crc32校验。