0 本文概述
本文将对通过终端cli在Ceph集群中创建image的流程进行解析
1 流程时序图
![](https://img.haomeiwen.com/i12862687/13c45b203423a1fd.png)
2 代码走读
2.1 命令行处理:/src/tools/
2.1.1 rbd.cc
rbd.cc提供main函数入口,作为主程序的入口,调用shell.cc中的execute()函数,对指令进行解析。
![](https://img.haomeiwen.com/i12862687/43c9f2bb09478619.png)
2.1.2 shell.cc
shell.cc解析命令行,并通过封装的Action类调用对应的文件,对应当前目录下的Action文件。对于create而言,这里调用create.cc文件。
2.1.3 Action/create.cc
![](https://img.haomeiwen.com/i12862687/542ff777a22b5b3f.png)
在execute()函数中调用librados和IoContext并初始化,之后调用do_create().
![](https://img.haomeiwen.com/i12862687/8385e91c956f8da1.png)
2.2 librbd层:src/librbd/
2.2.1 librbd.cc
librbd提供相应的接口,具体实现在interval.cc中,本文中create image操作使用create4()实现对create()接口的封装。
![](https://img.haomeiwen.com/i12862687/e2d36a7322b1340d.png)
![](https://img.haomeiwen.com/i12862687/567307e9ae56bb4d.png)
![](https://img.haomeiwen.com/i12862687/d1703de94c00c275.png)
通过create()接口,调用interval.cc中create()函数,interval.cc提供这些接口的实现(line 820 to line 877).
在create()函数中,librbd获取image id和相应的参数,并根据format的新旧使用调用不同的接口。v1现在已被废弃。使用v2来完成image create。
对于新format信息,调用当前目录中image目录中的CreateRequest.cc中send()函数进行调用。
2.2.2 /src/librbd/image/CreateRequest.cc
librbd中,将每个操作的具体实现都封装成了<operation>Request.cc/h文件,operation对应每个具体的操作,如当前的create操作的具体实现就是createRequest.cc/h中。
![](https://img.haomeiwen.com/i12862687/cac2d7bb19c888c2.png)
如上,头文件给出了类的定义,send实现在.cc文件中。头文件还给出了CreateRequest.cc实现image create的函数调用流程图。
![](https://img.haomeiwen.com/i12862687/c2444d259a4e6e88.png)
函数声明方面,函数和回调函数成对声明,回调函数以handle_开头区分。
![](https://img.haomeiwen.com/i12862687/628d6a1e4d659f55.png)
![](https://img.haomeiwen.com/i12862687/38b81b0bf8149ed9.png)
![](https://img.haomeiwen.com/i12862687/a913a7a78124f951.png)
上面的函数实现中可以看出,send()函数调用validate_pool()函数,在validate_pool()函数中,封装并调用回调函数handle_validate_pool()函数,校验rbd_directory对象是否存在,下一步的操作在回调函数中调用。其中aio_operate()实际执行回调函数。
![](https://img.haomeiwen.com/i12862687/a7cd2b2a243e4094.png)
validate_overwrite()作用是校验rbd_info的内容。
![](https://img.haomeiwen.com/i12862687/5a4b3de0c1e81203.png)
![](https://img.haomeiwen.com/i12862687/0c839f1ce673198f.png)
在handle_validate_overwrite()函数中,如果rbd_info存在并且内容为“overwrite validated”, 直接进入下一状态,调用create_id_object(),创建rbd_id.<image_name>对象。
![](https://img.haomeiwen.com/i12862687/e2c3564fdfaa715e.png)
create_id_object()函数中,先调用了cls_client::set_id()创建rbd_id,之后又使用回调函数,这里个人观点为cls的函数需要回调函数触发。在回调函数中,调用add_image_to_directory()函数进入下一状态。
![](https://img.haomeiwen.com/i12862687/e5021244cc0bf002.png)
add_image_to_directory()作用为在rbd_directory对象中加入该image的id和name。
![](https://img.haomeiwen.com/i12862687/aad4322df789b878.png)
negotiate_features()中,会首先获取所有的feature,之后触发回调函数,将返回的feature decode到all_features中,之后调用create_image(),进入下一状态。
![](https://img.haomeiwen.com/i12862687/f238b496e767f71b.png)
![](https://img.haomeiwen.com/i12862687/8eb40c9c33a5c4c4.png)
在create_image()中,通过cls注册的函数创建rbd_header对象,并设置omap中的值,调用回调函数完成创建。
![](https://img.haomeiwen.com/i12862687/87d0d94aa4cba8ab.png)
![](https://img.haomeiwen.com/i12862687/18a2a955adb2550a.png)
之后代码遵循类似的流程,都是通过cls_client注册的函数 + 回调函数完成该状态功能,并调用函数,进入下一状态。调用的函数顺序为:
set_strip_unit_count() -> handle_strip_unit_count() -> object_map_resize() -> handle_object_map_resize() -> fetch_mirror_mode() -> handle_fetch_mirror_mode() -> journal_create() -> handle_journal_create() -> mirror_image_enable() -> handle_mirror_image_enable()
在一系列的调用完成后,调用complete()函数,传入的参数为0。
![](https://img.haomeiwen.com/i12862687/3d75dfc7328a50ff.png)
在complete()函数中,释放数据对象上下文,调用CreateRequest回调函数,完成步骤。
2.2.3 cls/rbd/cls_rbd_client.cc
在前文的Request调用中,使用回调函数进入下一状态之前,会先调用cls模块进行注册,cls模块针对rbd的实现在cls_rbd_client.cc文件中。
![](https://img.haomeiwen.com/i12862687/1c6b64760bc72d46.png)
![](https://img.haomeiwen.com/i12862687/9e7c503a2643b54f.png)
![](https://img.haomeiwen.com/i12862687/6a7ce67864f8299a.png)
![](https://img.haomeiwen.com/i12862687/4751eebf6707ba87.png)
cls_client模块负责注册部分元数据操作。从调用的代码可以看出,cls_client下函数不会直接执行,而通过librados::ObjectOperation的exec()执行。在下一节可以看到,cls_client调用的操作在最终的OSDC层完成函数注册,但并不会实际调用。
需要注意的是,具体到image的创建,实际上只会记录一些image的基本信息。比如创建元数据对象rbd_id.foo和rbd_header.foo,对于image的真正数据对象rbd_data*,根本不会创建,这是因为ceph选择thin-provisioning这种凡事,可以做到秒级创建快设备,后段也可以超额分配容量。
2.3 librados层:src/librados/
librbd中cls_client的注册和回调函数的触发都在librados中实现。
2.3.1 librados.cc / librados.h
librados组件提供了cls_client注册函数需要的ObjectOperation的定义和接口实现,和IoctxImpl相关函数接口的封装。
cls_client调用的exec()函数的实现如下,函数中初始化ObjectOperationImpl实例,并调用其成员变量o的call()函数。
![](https://img.haomeiwen.com/i12862687/27ebbf66b5e2b816.png)
![](https://img.haomeiwen.com/i12862687/faec502f298b930e.png)
代码中成员变量o的类型ObjectOperation来自雨OSDC层的Objecter组件,具体实现将在OSDC层中进行进一步讨论。
librbd中,真正触发回调函数是通过aio_operate()完成的,librados中提供了该接口,具体实现是封装了IoctxImpl的aio_operate()接口,最终实现是在IoctxImpl中完成。
![](https://img.haomeiwen.com/i12862687/f693c4175fac55fe.png)
2.3.2 IoctxImpl.cc
![](https://img.haomeiwen.com/i12862687/26ffecd65128f439.png)
IoctxImpl中,::ObjectOperation op实际上是引用的src/osdc/Objecter.cc里面定义的类,初始化之后,通过op将消息发送给osd。真正使用osdc中的Objecter.cc中的组件的地方是operator()和aio_operate()函数。
2.4 OSDC 层:src/osdc
osdc层直接与osd进行通信,调用objecter.cc/objecter.h组件来实现消息的发送。
2.4.1 objecter.cc
objecter.cc提供对上层消息的封装,并将这些消息发送给osd,完成各项操作。
在librados层,cls_client调用的ObjectOperation的定义在objector.h中。
![](https://img.haomeiwen.com/i12862687/fc4eeabc47ff082e.png)
代码中各项成员变量的含义为
out_bl: 用于存放ops每个操作的输出内容
out_handler: 存放ops每个操作完成后执行的回调函数
out_rval: 存放ops每个操作的返回值
librados层中注册函数时调用的call函数的定义如下
![](https://img.haomeiwen.com/i12862687/99f367b3487baa63.png)
![](https://img.haomeiwen.com/i12862687/0fe0d7e07b14619c.png)
add_op()为其核心操作,作用是将ops数组中增加一个op,但不执行操作。
![](https://img.haomeiwen.com/i12862687/82375425baf40a65.png)
最终的执行操作是通过aio_operate()函数在osdc层调用的op_submit()函数完成的。
![](https://img.haomeiwen.com/i12862687/430717efaf988f88.png)
![](https://img.haomeiwen.com/i12862687/e9ebf4170cb328f6.png)
![](https://img.haomeiwen.com/i12862687/878f1e9c2f4ea684.png)
![](https://img.haomeiwen.com/i12862687/8a9856188b790fca.png)
![](https://img.haomeiwen.com/i12862687/638f6282bffc16f4.png)
从上述代码可以看出,在Objecter类中,遵循op_submit() -> _op_submit() -> _send_op()的调用顺序。
3 备注
[注] 参考链接ceph-librbd-create-image.html
[注] 从相关资料描述来看,RadosClient是其核心管理类,处理Rados层和Pool层管理,但是在当前的解析中并未体现RadosClient的作用,需要进一步的研究和分析。
网友评论