Ceph 撸源码系列(三):Ceph OSDC源码分析 (2 of 2)

本文是Ceph OSDC源码分析下篇,回顾上篇内容后,对数据分片进行分析,包括对象分片与objectextent对应关系、映射步骤等。还介绍了数据在objectcacher管理、objectextent与bufferhead映射关系、缓存状态,最后分析了数据读请求流程,包括各阶段操作及缓存命中情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载:Ceph OSDC源码分析(下篇)

回顾:Ceph OSDC源码分析(上篇)

数据分片分析

对象分片跟objectextent的对应关系有点复杂, 听小甲慢慢分析。

因为要使用OSDC就要用到用户态的客户端,就是使用fuse,但是内核态的fuse的模块对读写数据大小是进行了限制,一次最大是4K,一次最大是128K,也就是说我们如果像我们列子中要读取文件中1M到6M之内的内容不是一次传过来的,而是在fuse内核模块那边就已经分成了一个个128K的读,估计fuse用户态的库那边有一个线程池,所以每一次的128K都是并发由不同的线程进行处理。

因为上面fuse内核已经给我们的请求进行了分片,传过来的是一个个128K的读长度请求,对于我们例子中的情况,一个对象分片su是1M的情形,我们来细细的分析一下:

首先传来第一个128K的文件长度的读,那就是从偏移量为1M的开始读取长度为128K的文件数据,我们该怎么映射分片呢?

offset = 1M
len = 128K

步骤如下:

第一步:计算出偏移量的位置信息,这个位置信息我们要把它从一维的转化为三维的坐标,计算过程如下:

 

blockno = offset/su =1M/1M =1         块号,也就是分片号就是su1
stripeno = blockno /stripe_count = 1/3 =0    条带号,表示一个条带stripe0
stripepos  = blockno%stripe_count = 1%3 = 1  条带内偏移,就是在条带内的第二个对象上面
objectsetno = stripeno / stripes_per_object=0/3 =0   对象set号,表示objectset0
objectno = objectsetno*stripe_count + stripepos= 0*3+1对象号,就是分片所在的哪个对象

这样我们就把一维的坐标 转化为 三维的坐标

(su1) –>(objectset0, stripe0, stripepos=1)

 

 

第二步:修改分配内的偏移量

block_start = (stripeno % stripes_per_object) * su = (0%3)*1M =0  //这个表示的是文件操作偏移量在所对应的对象中的偏移
block_off = offset % su = 1M %1M =0  //这个表示的是文件操作偏移量在所对应的对象su中的偏移
x_offset = block_start + block_off = 0 + 0 //这个表示对象分片的偏移实际地址
max = su - block_off=1M -0 = 1M //这个分片还剩的最大容量
x_len = min(len,max)  //分片内的偏移量的最小值

 

上面公式太复杂,我们下面以几个列子来寻求普遍情况:

例子一:是上面的128K,从su1边界开始

 

可以看出

x_offset = 0
x_len=128k
然后生成一个objectextent,里面的offset=x_offset = 0,length=x_len=128k

 

例子二:如下情景

 

可以看出

x_offset = 64k
x_len=128k
然后生成一个objectextent,里面的offset=x_offset = 64k,length=x_len=128k

 


例子三:如下情景

虽然我们知道在我们假设的su为1M,fuse一次读分片是128K的情况下是不会出现这种情况的,但是我们可以调整su的大小,调整对象的大小,这样的情况就可以达到了。

 

这里我们假设去掉fuse读分片128k的限制,我们假设读了1M的数据,并且我们su为1M,现在我们要读上面所有绿色的部分。

 

对于前面三个分片,我们可以知道:

 

第一个分片在object1的第一个su中,不是一个整的分片;第二个分片是在object2中的第一个su中,完整的一个分片;第三个分片是在object0中的第二个su,是一个完整的分片;最后一个分片是在object1的第二个su中,我们可以知道,对于前三个分片,在分片结果集map<object_t,vector > object_extent会存放三个对象。

 

假设我们还没有映射最后一个分片,只是映射了三个分片,此时,object_extent下的状态是如下:

那对于第四个分片呢,会在object1下面再生成一个objectextent,这个里面的分片是不全的。

 

现在,问题来了:是不是一个su对应一个objectextent

 

答案是的,一个su对应一个objectextent,可能这个objectextent没有su那么大的范围,即len(objectextent)<=len(su)   ,而且在objectextent 结构中有一个  vector<pair<uint64_t,uint64_t>>buffer_extents   

这个是用来记录此分片相对于我们要读的偏移位置的偏移量,我们画个图解释一下。

 

buffer_extents里面的key是指的蓝色小格相对于offset的偏移,不过小甲在撸完代码之后,感觉基本上一个objectextent里面的buffer_extents只会存在一个pair,不会存在多个pair,一般要么就是一个su或者就是小于su。

 

数据在objectcaher管理

 

从上面的file_to_extent中得到映射后的结果集object_extent,这个是一个map,首先遍历取出所有的objectextent放在一个vector中。

 

然后在下面的readx函数中去读每一个映射后分片objectextent上面的内容,因为objectextent结构中包含了要读对象的名字和要读内容在这个对象中的迁移位置,所以就可以对每一个objectextent进行并发读取

 

但是在读的时候由于使用objectcacher缓存,我们可以先从缓存中查找看看我们要找的objectextent范围的内容是不是在缓存中,缓存命中在直接取出,不命中就需要去rados读。

 

映射关系

 

这里就有一个问题,就是objectextent如何跟bufferhead映射起来?接下来我们分析一下。

 

这主要是一个函数map_read,这个函数就是对objectextent做一个映射,这里面的映射也很有趣,我们来看一下,首先说明一下bufferhead是一个对象内部的片段,毫无规律的。

 

objectbufferhead之间的关系如下,这里的object严格说不是rados的object,是OSDC缓存中的对象,其实可以说是内存中与磁盘对应的数据结构。

 

 

上图中一个object中有三个bufferhead

下面分析几种情景来说明object、bufferheadobjectextent之间的关系

 

(1) objectextent的范围在现有的bufferhead之后:

这种情况下,会生成一个bufferhead,包含objectextent的范围,加入一个bufferhead之后变成这样:

(2) objectextent的范围在某一个bufferhead之内:

像上面这种情况,表示一部分已有映射,其实上面这种情况还有一部分在外面,最后变成这样

 

(3) objectextent的范围在间隔段之间

像上面这种情况,objectextent中间某一部分已经映射到对象的某部分,最后映射的结果如下

一般一个objectextent会对应到多个bufferhead上面。

 

缓存状态

 

这里我们只是做了映射关系,那如何看这些bufferhead中是不是都缓存了对象的数据呢?这个就是根据bufferhead的状态来进行,bufferhead有很多状态,我们来看一下

bufferhead有这几种状态,根据代码中的意识是当状态为clean,dirty,tx和zero时,可以认为缓存中是有对象数据的。

 

数据读请求分析

 

读操作请求一般会经过这几个过程

 

 (1) 首先去mds(metadata server)上面找到文件所对应的元数据,如果mds没有缓存inode,就要到osd上面去取元数据,此操作其实就时打开文件open

 

 (2) 去到元数据之后,inode里面包含了文件数据在osd上面的一些位置信息file_layout_t,这个里面主要是包含文件分片的一些参数,还有文件所在的池信息

 

 (3) 向osd发起了一个stat op的操作请求,返回文件的属性

 

 (4) 然后会发送一个create op的操作请求

 

 (5) 后面才会从fuse内核接受一个个128k的读请求

 

以一个128k的请求为例

 

假设我们缓存中刚开始是空的,好那我们在经过map_read之后我们我们流程图如下

 

(1)第一次读缓存未命中,要到osd上去取

 

 

因为缓存未命中,所以在bh_read中发起一个到osd去对象数据的op操作

 

(2) 客户端接收到osd读请求发送回来的响应

 

 

这里又开始进行第二次读,这次读就能在缓存中命中

 

(3) 第二次读流程,命中缓存

 

至此,一个读流程分析完毕。

 

||写在最后

 

小甲提醒,这一期讲的东西有点多,这里面还是很复杂的,读者需要静下心来结合代码好好分析,如果有疑问,请留言与小甲深入讨论

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值