Librbd是Ceph提供块存储的库,它实现了RBD接口,基于LIbrados实现了对RBD的基本操作。Librbd对于元数据的相关操作是通过cls_rbd实现的。cls_rbd是Cls的一个扩展模块,Cls允许用户自定义对象的操作接口和实现方法,为用户提供了一种比较直接的接口扩展方式。Librbd的数据读写相关操作则是通过直接Librados来直接访问。
Librbd 包含了rbd的相关操作,并发送给ImageRequestWQ类处理(队列),而后该类将其中的的请求发送给ImageRequest处理,ImageRequest将Image进行分片(将一个 块 分解成 对象 进行处理,Ceph的底层本质还是对象存储) 等操作后,将各个对象调用ObjectRequest类进行处理 ,每个ObjectRequest请求分别处理。ImageRequest和ObjectRequest下面都包含很多相关操作的子类,用子类实现了具体的接口。关于RBD的读写流程用了函数调用的流程图表示,思路更加清晰,并且最新版本函数、类的名字都改变了。
结构如图:
cls_rbd是Cls的一个扩展模块,Cls允许用户自定义对象的操作接口和实现方法,为用户提供了一种比较直接的接口扩展方式。
该模块下包含几个文件:
1)cls_rbd_types 主要是注册了rbd模块,以及自定义的一些方法
...
...
...
//对应的方法
int create(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
string object_prefix;
uint64_t features, size;
uint8_t order;
int64_t data_pool_id = -1;
try {
bufferlist::iterator iter = in->begin();
::decode(size, iter);
::decode(order, iter);
::decode(features, iter);
::decode(object_prefix, iter);
if (!iter.end()) {
::decode(data_pool_id, iter);
}
} catch (const buffer::error &err) {
return -EINVAL;
}
...
...
...
//注册模块以及方法
cls_register("rbd", &h_class);
cls_register_cxx_method(h_class, "create",
CLS_METHOD_RD | CLS_METHOD_WR,
create, &h_create);
...
...
...
2)cls_rbd_client 定义了客户端访问rbd函数。调用模块和客户端的读写操作流程类似,都是通过ioctx->ecex 函数,封装操作并发送给对应的OSD处理。见( 超链接留空 )
RBD是ceph中提供块存储的客户端服务,之所以说是客户端服务是相对于RADOS而言,RBD是基于librados API开发的一个块存储服务。Ceph底层的实现是对象存储,通过librbd实现了块存储的接口,对外提供块存储的服务。
所以,块存储在客户端也会被拆成n个对象来存储处理。
下面摘取
麦子迈:解析Ceph: Librbd–块存储库 中的基础名词介绍。
Image: 对应于LVM的Logical Volume,是能被attach/detach到VM的载体。在RBD中,Image的数据有多个Object组成。(image 镜像 可以作是块存储的呈现形式)
Snapshot: Image的某一个特定时刻的状态,只能读不能写但是可以将Image回滚到某一个Snapshot状态。Snapshot必定属于某一个Image。
关于 块存储 原理的一些东西,可以操作集群 实际看看:Ceph介绍之RBD实现原理
当在一个pool里面创建一个RBD对象的时候,会对应的生成其它的对象以及相关的元数据用于管理rbd。
另外还会创建数据对象 ,用于存储数据,因为Ceph存储的本质就对象存储,所以即使提供的块存储也要拆分成对象来管理。
librbd 模块下的源代码现在文件比较多。根据网上别人的总结。。。。
http://blog.csdn.net/scaleqiao/article/details/51165598
1)operation, 这个目录里实现了关于image的主要操作,包括flatten、resize、trim、snapshot create、snapshot remove、snapshot protect、snapshot unprotect、snapshot rename、snapshot rollback
2)librbd.cc,这个文件里并没有具体实现所有的接口,而其实大部分接口的具体实现是在internal.cc,除了I/O相关的接口,read/write。
3) ImageCtx.h和ImageCtx.cc,定义了image的上下文,包括image layout的初始化、卷/snap相关元数据的获取接口以及后来引入的rbd mirror的相关元数据结构(object map、journal 等)的创建。
4)AioImageRequestWQ.h/AioImageRequestWQ.cc、AioImageRequest.h/AioImageRequest.cc,实现了aio相关的I/O接口,进入rbd的read请求会转变成一个AioImageRead的实例,而write请求会变成一个AioImageWrite的对象。而这些对象最终会分别调到AioImageRead.send_request()和AbstractAioImageWrite::send_request()这两个函数,来处理IO请求(其中主路径中关于journal的部分可以先略过去)。
5) AioObjectRequest.h/AioObjectRequest.cc,I/O请求会通过Striper::file_to_extents()被影射成针对某个/某些对象的操作,这些操作都被定义/实现在这两个文件中
6) AioCompletion.h/AioCompletion.cc,这两个文件实现了Aio的回调处理逻辑。上层应用可以通过rbd_aio_create_completion()创建一个AioCompletion的对象,并传入自己的回调函数。这样当具体操作结束之后,AioCompletion对象会调用上层应用的回调函数
7) LibrbdWriteback.h/LibrbdWriteback.cc,这两个文件主要是通过现有的ObjectCacher机制,在librbd中支持Writeback这种I/O方式
8) ImageWatcher.h/ImageWatcher.cc,这是后来引入的watch/notify机制,用于增加了管理image exlusive lock的支持
9) 日志 Journal*,这些文件主要实现rbd mirror的功能
10)CopyupRequest.h/CopyupRequest.cc,这个主要用于处理clone卷的逻辑
看完思路清晰不少。但是目前最新的版本而言,改动了一些,主要是名字的AIo都没了。 但是跳转的处理逻辑依然还是差不多 集中在 ImageRequestWQ、ImageRequest、ObjectRequest之中。
rbd有很多个创建函数。根据传入的参数不同来调用不同的函数。其过程比较简单,都是调用librbd::create来设置相应的参数。RBD的创建过程,其本质是调用Cls的RBD模块设置相关的元数据信息。相关的元数据的操作函数也一样。
int RBD::create(IoCtx& io_ctx, const char *name, uint64_t size, int *order)
{
TracepointProvider::initialize(get_cct(io_ctx));
tracepoint(librbd, create_enter, io_ctx.get_pool_name().c_str(), io_ctx.get_id(), name, size, *order);
int r = librbd::create(io_ctx, name, size, order);//设置参数
tracepoint(librbd, create_exit, r, *order);
return r;
}
...
...
...
int RBD::create4(IoCtx& io_ctx, const char *name, uint64_t size,
ImageOptions& opts)
{
TracepointProvider::initialize(get_cct(io_ctx));
tracepoint(librbd, create4_enter, io_ctx.get_pool_name().c_str(), io_ctx.get_id(), name, size, opts.opts);
int r = librbd::create(io_ctx, name, "", size, opts, "", "", false);//设置参数
tracepoint(librbd, create4_exit, r);
return r;
}
在Imagectx中处理了Image的上下文自信,每一个Image中都有一个AioImageRequestWQ ,用于处理Image的操作,他是一个工作队列,通过线程池来实现异步请求处理。
RBD中包含几个数据操作的关键的类,其中 ObjectRequest用于对某一个对象的处理的基类,而ImageRequest则是处理Image的基类。图中可以看到他们都有不少子类,其中 AbstractImageWrite下面还有 imageWrite、ImageDiscard等子类,图中为画出。而 ImageRequest和ObjectRequest的之间的联系 是在当一个Image的操作跨越了多个对象的时候(一个块会被拆成多个对象进行操作,Ceph本质是对象存储),在每个对象中产生一个ObjectRequest 请求,用于在各自的对象中各自处理。
根据该函数调用关系图,一一展开看卡各个函数,其中用写流程来看,读流程操作类似。
1) rbd_aio_write 处于Librbd里面,librbd 是对外提供rbd接口的库,所以其中包含了较多的rbd请求。rbd的请求都是从这里开始的。
extern "C" int rbd_aio_write(rbd_image_t image, uint64_t off, size_t len,
const char *buf, rbd_completion_t c)
{
librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
librbd::RBD::AioCompletion *comp = (librbd::RBD::AioCompletion *)c;
tracepoint(librbd, aio_write_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, off, len, buf, comp->pc);
bufferlist bl;
bl.push_back(create_write_raw(ictx, buf, len));
//调用ImageRequestWQ::aio_write 来生成ImageRequest对象
ictx->io_work_queue->aio_write(get_aio_completion(comp), off, len,
std::move(bl), 0);
tracepoint(librbd, aio_write_exit, 0);
return 0;
}
2) ImageRequestWQ::aio_write 处于 ImageRequestWQ中,
template <typename I>
void ImageRequestWQ<I>::aio_write(AioCompletion *c, uint64_t off, uint64_t len,
bufferlist &&bl, int op_flags,
bool native_async) {
CephContext *cct = m_image_ctx.cct;
ZTracer::Trace trace;
if (m_image_ctx.blkin_trace_all) {
trace.init("wq: write", &m_image_ctx.trace_endpoint);
trace.event("init");
}
c->init_time(util::get_image_ctx(&m_image_ctx), AIO_TYPE_WRITE);
ldout(cct, 20) << "ictx=" << &m_image_ctx << ", "
<< "completion=" << c << ", off=" << off << ", "
<< "len=" << len << ", flags=" << op_flags << dendl;
if (native_async && m_image_ctx.event_socket.is_valid()) {
c->set_event_notify(true);
}
if (!start_in_flight_io(c)) {
return;
}
RWLock::RLocker owner_locker(m_image_ctx.owner_lock);
if (m_image_ctx.non_blocking_aio || writes_blocked()) {
//将写请求 的 ImageRequest 加入队列中
queue(ImageRequest<I>::create_write_request(
m_image_ctx, c, {{off, len}}, std::move(bl), op_flags, trace));
} else {
c->start_op();
//直接调用ImageRequest::aio_write处理写请求
ImageRequest<I>::aio_write(&m_image_ctx, c, {{off, len}},
std::move(bl), op_flags, trace);
finish_in_flight_io();
}
trace.event("finish");
}
3)在2)中加入队列里面的额请求,都会用process来进行处理,最后调用ImageRequest::send 函数来处理
template <typename I>
void ImageRequestWQ::process(ImageRequest *req) {
CephContext *cct = m_image_ctx.cct;
ldout(cct, 20) << "ictx=" << &m_image_ctx << ", "
<< "req=" << req << dendl;
req->send();//调用处理
finish_queued_io(req);
if (req->is_write_op()) {
finish_in_flight_write();
}
delete req;
finish_in_flight_io();
}
4) 对于2)中直接调用该函数处理的,最后也是直接调用ImageWriteRequest::send()处理
template <typename I>
void ImageRequest::aio_write(I *ictx, AioCompletion *c,
Extents &&image_extents, bufferlist &&bl,
int op_flags,
const ZTracer::Trace &parent_trace) {
ImageWriteRequest req(*ictx, c, std::move(image_extents), std::move(bl),
op_flags, parent_trace);
req.send();//处理
}
5)类 ImageWriteRequest 继承自 AbstractImageWriteRequest 所以最后调用的是,AbstractImageWriteRequest的send_request()函数。并且,后面调用的函数都类似,都是在ImageWriteRequest类的函数中调用AbstractImageWriteRequest对应的函数。因此直接看AbstractImageWriteReques对应的函数。
ImageRequest.cc
template <typename I>
void AbstractImageWriteRequest<I>::send_request() {
I &image_ctx = this->m_image_ctx;
CephContext *cct = image_ctx.cct;
RWLock::RLocker md_locker(image_ctx.md_lock);
bool journaling = false;
AioCompletion *aio_comp = this->m_aio_comp;
uint64_t clip_len = 0;
ObjectExtents object_extents;
::SnapContext snapc;
{
// prevent image size from changing between computing clip and recording
// pending async operation
RWLock::RLocker snap_locker(image_ctx.snap_lock);
if (image_ctx.snap_id != CEPH_NOSNAP || image_ctx.read_only) {
aio_comp->fail(-EROFS);
return;
}
for (auto &extent : this->m_image_extents) {
if (extent.second == 0) {
continue;
}
// map to object extents
//!!!!!!!将image 分片,块->对象的映射(块存储 的image 依然要用对象存储来处理)
Striper::file_to_extents(cct, image_ctx.format_string, &image_ctx.layout,
extent.first, extent.second, 0, object_extents);
clip_len += extent.second;
}
snapc = image_ctx.snapc;
journaling = (image_ctx.journal != nullptr &&
image_ctx.journal->is_journal_appending());
}
int ret = prune_object_extents(object_extents);
if (ret < 0) {
aio_comp->fail(ret);
return;
}
if (!object_extents.empty()) {
uint64_t journal_tid = 0;
aio_comp->set_request_count(
object_extents.size() + get_object_cache_request_count(journaling));
//将分成的对象 分别生成 ObjectRequests 对象 调用send_object_requests 各个对象各自处理
ObjectRequests requests;
send_object_requests(object_extents, snapc,
(journaling ? &requests : nullptr));
if (journaling) {
// in-flight ops are flushed prior to closing the journal
assert(image_ctx.journal != NULL);
journal_tid = append_journal_event(requests, m_synchronous);
}
if (image_ctx.object_cacher != NULL) {
send_object_cache_requests(object_extents, journal_tid);
}
} else {
// no IO to perform -- fire completion
aio_comp->unblock();
}
update_stats(clip_len);
aio_comp->put();
}
6) AbstractImageWriteRequest::send_object_requests 用于发送请求,如果 journaling 则 将请求加入队列中 否则直接 调用AbstractObjectWriteRequest::send() 发送请求。
template <typename I>
void AbstractImageWriteRequest::send_object_requests(
const ObjectExtents &object_extents, const ::SnapContext &snapc,
ObjectRequests *object_requests) {
I &image_ctx = this->m_image_ctx;
CephContext *cct = image_ctx.cct;
AioCompletion *aio_comp = this->m_aio_comp;
for (ObjectExtents::const_iterator p = object_extents.begin();
p != object_extents.end(); ++p) {
ldout(cct, 20) << "oid " << p->oid << " " << p->offset << "~" << p->length
<< " from " << p->buffer_extents << dendl;
C_AioRequest *req_comp = new C_AioRequest(aio_comp);
//ObjectRequestHandle是ObjectRequest的父类
ObjectRequestHandle *request = create_object_request(*p, snapc,
req_comp);
// if journaling, stash the request for later; otherwise send
//如果 journaling 则 将请求加入队列中 否则直接 调用AbstractObjectWriteRequest::send()
if (request != NULL) {
if (object_requests != NULL) {
object_requests->push_back(request);
} else {
request->send();
}
}
}
}
7)AbstractObjectWriteRequest::send()
先处理object_map
相关 ,然后调用AbstractObjectWriteRequest::pre_write_object_map_update()
,然后最终调用AbstractObjectWriteRequest::write_object()
发送请求,将请求发送到librados处理。
ObjectRequest.cc
template <typename I>
void AbstractObjectWriteRequest<I>::send() {
I *image_ctx = this->m_ictx;
ldout(image_ctx->cct, 20) << this->get_op_type() << " " << this->m_oid << " "
<< this->m_object_off << "~" << this->m_object_len
<< dendl;
{
RWLock::RLocker snap_lock(image_ctx->snap_lock);
if (image_ctx->object_map == nullptr) {
m_object_may_exist = true;
} else {
// should have been flushed prior to releasing lock
assert(image_ctx->exclusive_lock->is_lock_owner());
m_object_may_exist = image_ctx->object_map->object_may_exist(
this->m_object_no);
}
}
if (!m_object_may_exist && is_no_op_for_nonexistent_object()) {
ldout(image_ctx->cct, 20) << "skipping no-op on nonexistent object"
<< dendl;
this->async_finish(0);
return;
}
//最终调用 该函数
pre_write_object_map_update();
}
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
template <typename I>
void AbstractObjectWriteRequest<I>::pre_write_object_map_update() {
I *image_ctx = this->m_ictx;
image_ctx->snap_lock.get_read();
if (image_ctx->object_map == nullptr || !is_object_map_update_enabled()) {
image_ctx->snap_lock.put_read();
write_object();
return;
}
if (!m_object_may_exist && m_copyup_enabled) {
// optimization: copyup required
image_ctx->snap_lock.put_read();
copyup();
return;
}
uint8_t new_state = this->get_pre_write_object_map_state();
ldout(image_ctx->cct, 20) << this->m_oid << " " << this->m_object_off
<< "~" << this->m_object_len << dendl;
image_ctx->object_map_lock.get_write();
if (image_ctx->object_map->template aio_update<
AbstractObjectWriteRequest<I>,
&AbstractObjectWriteRequest<I>::handle_pre_write_object_map_update>(
CEPH_NOSNAP, this->m_object_no, new_state, {}, this->m_trace, this)) {
image_ctx->object_map_lock.put_write();
image_ctx->snap_lock.put_read();
return;
}
image_ctx->object_map_lock.put_write();
image_ctx->snap_lock.put_read();
write_object();
}
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
template <typename I>
void AbstractObjectWriteRequest<I>::handle_pre_write_object_map_update(int r) {
I *image_ctx = this->m_ictx;
ldout(image_ctx->cct, 20) << "r=" << r << dendl;
assert(r == 0);
write_object();
}
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
template <typename I>
void AbstractObjectWriteRequest<I>::write_object() {
I *image_ctx = this->m_ictx;
ldout(image_ctx->cct, 20) << dendl;
librados::ObjectWriteOperation write;
if (m_copyup_enabled) {
ldout(image_ctx->cct, 20) << "guarding write" << dendl;
write.assert_exists();
}
add_write_hint(&write);
add_write_ops(&write);
assert(write.size() != 0);
librados::AioCompletion *rados_completion = util::create_rados_callback<
AbstractObjectWriteRequest<I>,
&AbstractObjectWriteRequest<I>::handle_write_object>(this);
int r = image_ctx->data_ctx.aio_operate(
this->m_oid, rados_completion, &write, m_snap_seq, m_snaps,
(this->m_trace.valid() ? this->m_trace.get_info() : nullptr));
assert(r == 0);
rados_completion->release();
}