本节书摘来自华章出版社《Ceph源码分析》一书中的第2章,第2.2节Buffer,作者常涛,更多章节内容可以访问云栖社区“华章计算机”公众号查看
2.2 BufferBuffer就是一个命名空间,在这个命名空间下定义了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校验。