本節書摘來自華章出版社《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校驗。