Ceph源码解析:读写流程(3)

5. image = rbd.Image(ioctx,'myimage'),创建image结构,这里该结构将myimage与ioctx 联系起来,后面可以通过image结构直接找到ioctx。这里会将ioctx复制两份,分为为data_ioctx和md_ctx。见明知意,一个用来处理rbd的存储数据,一个用来处理rbd的管理数据。

流程图:

143540_OSPk_2460844

1. image.write(data,0),通过image开始了一个写请求的生命的开始。这里指明了request的两个基本要素 buffer=data 和 offset=0。由这里开始进入了ceph的世界,也是c++的世界。

由image.write(data,0)  转化为librbd.cc 文件中的Image::write() 函数,来看看这个函数的主要实现

ssize_t Image::write(uint64_t ofs, size_t len, bufferlist& bl) {      ImageCtx *ictx = (ImageCtx *)ctx;    int r = librbd::write(ictx, ofs, len, bl.c_str(), 0);    return r;      }

2. 该函数中直接进行分发给了librbd::wrte的函数了。跟随下来看看librbd::write中的实现。该函数的具体实现在internal.cc文件中。

ssize_t write(ImageCtx *ictx, uint64_t off, size_t len, const char *buf, int op_flags) {    Context *ctx = new C_SafeCond(&mylock, &cond, &done, &ret);  //---a    AioCompletion *c = aio_create_completion_internal(ctx, rbd_ctx_cb);//---b    r = aio_write(ictx, off, mylen, buf, c, op_flags);  //---c      while (!done)            cond.Wait(mylock);  // ---d }

---a.这句要为这个操作申请一个回调操作,所谓的回调就是一些收尾的工作,信号唤醒处理。

---b。这句是要申请一个io完成时 要进行的操作,当io完成时,会调用rbd_ctx_cb函数,该函数会继续调用ctx->complete()。

---c.该函数aio_write会继续处理这个请求。

---d.当c句将这个io下发到osd的时候,osd还没请求处理完成,则等待在d上,直到底层处理完请求,回调b申请的 AioCompletion, 继续调用a中的ctx->complete(),唤醒这里的等待信号,然后程序继续向下执行。

3.再来看看aio_write 拿到了 请求的offset和buffer会做点什么呢?

int aio_write(ImageCtx *ictx, uint64_t off, size_t len, const char *buf,            AioCompletion *c, int op_flags) {      //将请求按着object进行拆分      vector<ObjectExtent> extents;      if (len > 0)        {          Striper::file_to_extents(ictx->cct, ictx->format_string,                        &ictx->layout, off, clip_len, 0, extents);  //---a      }        //处理每一个object上的请求数据      for (vector<ObjectExtent>::iterator p = extents.begin(); p != extents.end(); ++p)        {            C_AioWrite *req_comp = new C_AioWrite(cct, c); //---b            AioWrite *req = new AioWrite(ictx, p->oid.name, p->objectno, p- >offset,bl,….., req_comp);    //---c            r = req->send();    //---d      } }

根据请求的大小需要将这个请求按着object进行划分,由函数file_to_extents进行处理,处理完成后按着object进行保存在extents中。file_to_extents()存在很多同名函数注意区分。这些函数的主要内容做了一件事儿,那就对原始请求的拆分。

一个rbd设备是有很多的object组成,也就是将rbd设备进行切块,每一个块叫做object,每个object的大小默认为4M,也可以自己指定。file_to_extents函数将这个大的请求分别映射到object上去,拆成了很多小的请求如下图。最后映射的结果保存在ObjectExtent中。

原本的offset是指在rbd内的偏移量(写入rbd的位置),经过file_to_extents后,转化成了一个或者多个object的内部的偏移量offset0。这样转化后处理一批这个object内的请求。

4. 再回到 aio_write函数中,需要将拆分后的每一个object请求进行处理。

---b.为写请求申请一个回调处理函数。

---c.根据object内部的请求,创建一个叫做AioWrite的结构。

---d.将这个AioWrite的req进行下发send().

5. 这里AioWrite 是继承自 AbstractWrite ,AbstractWrite 继承自AioRequest类,在AbstractWrite 类中定义了send的方法,看下send的具体内容.

int AbstractWrite::send()  {      if (send_pre())          //---a } #进入send_pre()函数中 bool AbstractWrite::send_pre() {       m_state = LIBRBD_AIO_WRITE_PRE;  // ----a      FunctionContext *ctx =    //----b            new FunctionContext( boost::bind(&AioRequest::complete, this, _1));      m_ictx->object_map.aio_update(ctx); //-----c }

---a.修改m_state 状态为LIBRBD_AIO_WRITE_PRE。

---b.申请一个回调函数,实际调用AioRequest::complete()

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/bccb0f79f96239680a8dd2c94f779512.html