關于Ceph中Bufferlist的設計與使用
如果非要在整個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這三個類的相互關系可以用下面這個圖來表示:
圖中藍色的表示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等開源存儲項目的核心代碼貢獻者。