本文简介rbd中数据的写入过程。
do_import(librbd::RBD &rbd, librados::IoCtx& io_ctx,
const char *imgname, int *order, const char *path,
int format, uint64_t features, uint64_t size,
uint64_t stripe_unit, uint64_t stripe_count)
>open(path, O_RDONLY) //首先打开要导入的文件
>fstat(fd, &stat_buf) //获取该文件的元信息,主要关注size
>do_create(rbd, io_ctx, imgname, size, order, format, features,stripe_unit, stripe_count) //为上传的文件创建一个image
>rbd.open(io_ctx, image, imgname) //打开该image
>read(fd, p + blklen, reqlen)) //从本地文件中读取指定长度的数据,放入到p执行的字符数组中。
>bl.append(p, blklen) //把读取到的数据放入到bufferlist中
>new AioImportContext(*throttle, image, bl, image_pos) //写入读取的文件到image中(在回调对象中实现) image_pos是相对于image
>image.aio_write2(m_offset, m_bufferlist.length(), m_bufferlist,m_aio_completion, op_flags) //m_offset相对于image
>submit_aio_write(ictx, off, len, bl.c_str(), get_aio_completion(c),op_flags) //rbd的写入提供两种写入方式:1.异步阻塞 2.异步非阻塞(两者并列)
>ictx->aio_work_queue->queue(new C_AioWriteWQ(ictx, off, len, buf, c,op_flags)) //异步非阻塞,将写入工作放入ImageCtx::aio_work_queue队列中
aio_work_queue是ContextWQ类的实例,该类继承于ThreadPool::PointerWQ->ThreadPool::WorkQueue_;有aio_work_queue = new ContextWQ("librbd::aio_work_queue",cct->_conf->rbd_op_thread_timeout,thread_pool_singleton);可以看出该队列中的元素会由ThreadPoolSingleton线程池中的线程来处理
由于元素C_AioWriteWQ是一个回调对象,在处理的时候,通过调用该类的complete方法,再调用finish方法,最后调用librbd::aio_write(m_ictx, m_off, m_len, m_buf, m_comp, m_op_flags),来处理。
>librbd::aio_write(ictx, off, len, buf, c, op_flags) //异步阻塞的调用方式
>Striper::file_to_extents(ictx->cct, ictx->format_string,&ictx->layout, off, clip_len, 0, extents)
>file_to_extents(cct, object_format, layout, offset, len, trunc_size,object_extents, buffer_offset) //将读取到的数映射到相应的对象上,被映射的对象会有一组vector<ObjectExtent>, 每个OjectExtent标记image的某段范围映射到该对象上。
>file_to_extents(cct, object_format, layout, offset, len, trunc_size,object_extents, buffer_offset)
>assimilate_extents(object_extents, extents) //把不同对象的ObjectExtent,合并到同一个vector<ObjectExtent>
>bufferlist bl;for(vector<ObjectExtent> for(ObjectExtent.buffer_extents){bl.append(buf + q->first, q->second)}) //把ObjectExtend中标记的image范围合并到一个bufferlist中。
>ictx->write_to_cache(p->oid, bl, p->length, p->offset, req_comp, op_flags) //rbd支持Cache,该调用是使用cache方式
>object_cacher->writex(wr, object_set, onfinish)
>AioWrite *req = new AioWrite(ictx, p->oid.name, p->objectno, p->offset, bl, snapc, req_comp) //禁用cache方式
>req->send()
>send_pre()
>send_write()
>add_write_ops(&m_write) //初始化Objecter::ObjectOperation对象
>m_ictx->data_ctx.aio_operate(m_oid, rados_completion, &m_write,m_snap_seq, m_snaps)
>io_ctx_impl->aio_operate(obj, (::ObjectOperation*)o->impl, c->pc,snapc, 0)
>objecter->mutate(oid, oloc, *o, snap_context, ut, flags, onack, oncommit,&c->objver) //进入Objecter层
>prepare_mutate_op(oid, oloc, op, snapc, mtime, flags, onack, oncommit, objver) //封装成Op
>op_submit(o)
>_op_submit_with_budget(op, lc, ctx_budget) //统计该Op的预算
>_op_submit(op, lc) //提交Op
当rbd做写入操作时:
1.缓存要写入的数(off(in image)、len、data)
2.计算该操作落到具体哪个对象上(通过off、image size、stripe,获得对象(off(in object)、len、data))
3.将要写入的对象封装在AioWrite对象中,并进一步放入librados::ObjecterOperation的 Objecter.h/ObjectOperation中
4.通过image context进入IO context再进入objecter层,ObjectOperation转变成Op
5.计算Op对应的pg、osd,获得session,进入massenger层,通过PipeConnect.Pipe,发送到客户端。