ceph minic版本的bluestore默认使用BitmapFreelistManager来管理磁盘空闲空间,并将磁盘空间使用情况固化到rocksdb。同时bluestore使用StupidAllocator来分配磁盘空间。在bluestore申请空间时要BitmapFreelistManager和StupidAllocator配合完成。

关于FreelistManager,在osd上电时,会调用_open_fm来初始化磁盘使用情况,_open_fm函数调用栈如下

    fm = FreelistManager::create(cct, freelist_type, db, PREFIX_ALLOC);return new BitmapFreelistManager(cct, kvdb, "B", "b");if (create)fm->create(bdev->get_size(), (int64_t)min_alloc_size, t); //运行BitmapFreelistManager::createbytes_per_block = granularity;  //因为一次分配min_alloc_size大小的空间size = p2align(new_size, bytes_per_block);  //往下对齐 即返回不大于new_size的可以整除bytes_per_block的数blocks_per_key = cct->_conf->bluestore_freelist_blocks_per_key;  //128个_init_misc();blocks = size / bytes_per_block;if (blocks / blocks_per_key * blocks_per_key != blocks) blocks = (blocks / blocks_per_key + 1) * blocks_per_key; //如果size不能被blocks_per_key整除,就向上扩大blocks_xor(size, blocks * bytes_per_block - size, txn); //设置人为造成的多出的空间已使用txn->set(meta_prefix, "bytes_per_block", bl);txn->set(meta_prefix, "blocks_per_key", bl);txn->set(meta_prefix, "blocks", bl);txn->set(meta_prefix, "size", bl);uint64_t reserved = round_up_to(std::max<uint64_t>(SUPER_RESERVED, min_alloc_size), min_alloc_size);   //SUPER_RESERVED 8192fm->allocate(0, reserved, t); //0-reserved区间标记使用_xor(offset, length, txn); if (cct->_conf->bluestore_bluefs)//bluefs使用的磁盘空间用bluefs_extents标记,不标记在freelistinterval_set<uint64_t>::iterator p = bluefs_extents.begin();  //open_db中bluefs_extents.insert(start, initial);//block extents owned by bluefsreserved = round_up_to(p.get_start() + p.get_len(), min_alloc_size);t->set(PREFIX_SUPER, "bluefs_extents", bl);db->submit_transaction_sync(t);fm->init()  //从rocksdb中加载bytes_per_block、blocks、size、blocks_per_key等,如果不是创建osd,则会运行这个

首先创建FreelistManager,因为默认类型是"bitmap",所以创建BitmapFreelistManager。如果是创建osd,则create是True,这里会初始化一些设置,比如bytes_per_block、size等,并将这些参数固化到rocksdb。同时标记磁盘前SUPER_RESERVED(给label和bluefs super block使用)字节空间已经使用。_xor函数就是反转某段磁盘空间的使用状态,同时将使用情况固化到rocksdb。另外bluefs使用的磁盘空间用bluefs_extents标记,不标记在freelist。
如果不是创建osd,则直接利用fm->init()来从rocksdb中读取size,bytes_per_block等配置。

BitmapFreelistManager设置完毕后,开始调用_open_alloc来初始化StupidAllocator,_open_alloc的函数调用栈如下:

alloc = Allocator::create(cct, cct->_conf->bluestore_allocator, bdev->get_size(), min_alloc_size); //"stupid"return new StupidAllocator(cct);
fm->enumerate_reset();
while (fm->enumerate_next(&offset, &length))alloc->init_add_free(offset, length);_insert_free(offset, length);unsigned bin = _choose_bin(len);while (true)free[bin].insert(off, len, &off, &len); //free大小为10,在StupidAllocator构造函数中初始化unsigned newbin = _choose_bin(len);if (newbin == bin)    break;free[bin].erase(off, len);bin = newbin;num_free += length;
for (auto e = bluefs_extents.begin(); e != bluefs_extents.end(); ++e) alloc->init_rm_free(e.get_start(), e.get_len());

bluestore默认使用StupidAllocator来分配磁盘空间。循环调用BitmapFreelistManager::enumerate_next来获取磁盘下一段空闲块,然后利用StupidAllocator::init_add_free来将获取到的块插入到free中,free的定义如下

std::vector<interval_set_t> free;  ///< leading-edge copy
typedef interval_set<uint64_t,interval_set_map_t> interval_set_t;
typedef btree::btree_map<uint64_t,uint64_t,std::less<uint64_t>,allocator_t> interval_set_map_t;

可以看到free是一个vector(大小为10),其元素类型包含一个btree结构,因此StupidAllocator是用btree来保存空闲块的,free中包含了10个btree。_choose_bin是计算块大小的数量级,目的为了将相同数量级大小的块插入到同一棵btree中。_insert_freej就是将[off,off+len]的磁盘空间插入到btree中,如果btree中有和[off,off+len]空间连续的其他空间,就将这两个空间合并,并返回最新合并后的空间的开始地址和长度,如果合并后的长度的数量级仍属于这个btree就退出,反之就从该btree中删除这个较大的合并后的空间,并插入到对应数量级的btree中。

分配空间
根据bluestore写操作中的处理来看bluestore如何分配空间的
在写操作中的_do_alloc_write函数中,会调用StupidAllocator::allocate来分配btree中的空闲空间,其调用栈如下:

alloc->allocate(need, min_alloc_size, need, 0, &prealloc);while (allocated_size < want_size)allocate_int(std::min(max_alloc_size, (want_size - allocated_size)), alloc_unit, hint, &offset, &length);uint64_t want = std::max(alloc_unit, want_size);    int bin = _choose_bin(want);   //我感觉是计算want处于的一个数量级,最大为10,因为free vector代表10个数量级len = orig_len / cct->_conf->bdev_block_size;bin = std::min((int)cbits(len), (int)free.size() - 1);int orig_bin = bin;auto p = free[0].begin();//free中一个元素代表一个数量级,free总共有10个树if (!hint)hint = last_alloc; //hint设置为上次已分配的磁盘的偏移        if (hint)for (bin = orig_bin; bin < (int)free.size(); ++bin) {p = free[bin].lower_bound(hint);//寻找不小于上次分配的地址while (p != free[bin].end())if (_aligned_len(p, alloc_unit) >= want_size) //找到足够的空间goto found;++p;for (bin = orig_bin; bin < (int)free.size(); ++bin)p = free[bin].begin();auto end = hint ? free[bin].lower_bound(hint) : free[bin].end();while (p != end)if (_aligned_len(p, alloc_unit) >= want_size)goto found;p++;if (hint)for (bin = orig_bin; bin >= 0; --bin)//在前面的树里查找p = free[bin].lower_bound(hint); while (p != free[bin].end())if (_aligned_len(p, alloc_unit) >= alloc_unit)goto found;++p;for (bin = orig_bin; bin >= 0; --bin)p = free[bin].begin();auto end = hint ? free[bin].lower_bound(hint) : free[bin].end();while (p != end) if (_aligned_len(p, alloc_unit) >= alloc_unit)goto found;++p;uint64_t skew = p.get_start() % alloc_unit;if (skew)skew = alloc_unit - skew;*offset = p.get_start() + skew; //从后面最近的按照alloc_unit对齐的地址开始*length = std::min(std::max(alloc_unit, want_size), p2align((p.get_len() - skew), alloc_unit));free[bin].erase(*offset, *length); //从树中去掉这块区域if (*offset && free[bin].contains(*offset - skew - 1, &off, &len))  //包含为了对齐,头部浪费的空间int newbin = _choose_bin(len);if (newbin != bin)  //如果数量级没有变化,则不需要变换位置free[bin].erase(off, len);_insert_free(off, len);if (free[bin].contains(*offset + *length, &off, &len))//包含为了对齐,尾部浪费的空间int newbin = _choose_bin(len);if (newbin != bin)//如果数量级没有变化,则不需要变换位置free[bin].erase(off, len);_insert_free(off, len);last_alloc = *offset + *length; //更新上次被分配的偏移地址

StupidAllocator分配空间时优先分配上次分配空间末尾地址后的空间。

在_do_alloc_write函数中还会把分配好的pextent插入到TransContext中的allocated中,如下

for (auto& p : extents) txc->allocated.insert(p.offset, p.length);//offset为在磁盘上的偏移

此时已分配空间只是从StupidAllocator的btree中删除记录,并没有固化到rocksdb中,bluestore是在_txc_finalize_kv中将txc->allocated中的分配记录写到rocksdb中的,如下‘

_txc_finalize_kv(txc, txc->t);interval_set<uint64_t> *pallocated = &txc->allocated;interval_set<uint64_t> *preleased = &txc->released;for (interval_set<uint64_t>::iterator p = pallocated->begin(); p != pallocated->end(); ++p)fm->allocate(p.get_start(), p.get_len(), t);for (interval_set<uint64_t>::iterator p = preleased->begin(); p != preleased->end();  ++p)fm->release(p.get_start(), p.get_len(), t);

其中会调用BitmapFreelistManager::allocate函数将分配的空间记录写到rocksdb中

ceph bluestore 磁盘空间管理源码解析相关推荐

  1. 动态代理及JDK代理源码解析

    动态代理及JDK代理源码解析 参考:JDK动态代理-超详细源码分析 - 简书 (jianshu.com) 文章目录 动态代理及JDK代理源码解析 一.为什么需要动态代理 什么是代理模式? 静态代理: ...

  2. PCL 实现 SAC_IA 算法原理源码解析

    PCL 实现 SAC_IA 算法原理源码解析 采样一致性算法(SAC_IA)用于点云粗配准中,配准效果还不错,PCL 中也实现了该算法,本文深入 PCL 源码,学习一下 SAC_IA 算法在 PCL ...

  3. vue双向绑定原理源码解析

    当我们学习angular或者vue的时候,其双向绑定为我们开发带来了诸多便捷,今天我们就来分析一下vue双向绑定的原理. 简易vue源码地址:https://github.com/maxlove123 ...

  4. Spring注解配置工作原理源码解析

    一.背景知识 在[Spring实战]Spring容器初始化完成后执行初始化数据方法一文中说要分析其实现原理,于是就从源码中寻找答案,看源码容易跑偏,因此应当有个主线,或者带着问题.目标去看,这样才能最 ...

  5. 六、Dubbo协议模块原理源码解析

    课程概要: RPC协议基本组成 RPC协议报文编码与实现详解 Dubbo中所支持RPC协议与使用 RPC协议基本组成 RPC 协议名词解释 在一个典型RPC的使用场景中,包含了服务发现.负载.容错.网 ...

  6. JDK动态代理源码解析

    分析版本jdk1.8 在分析jdk动态代理之前,先来了解java WeakReference弱引用的使用.运行期创建目标对象的代理非常耗时,使用缓存来存储生成的代理类显得尤为重要.jdk动态代理使用弱 ...

  7. 单例设计模式-Enum枚举单例、原理源码解析以及反编译实战

    package com.learn.design.pattern.creational.singleton;/*** 这个类是Enum类型* 这个枚举非常简单* 枚举类是Object* 他在多线程的时 ...

  8. java怎么让main方法不退出_JAVA线程池原理源码解析—为什么启动一个线程池,提交一个任务后,Main方法不会退出?...

    public static void main(String[] args) { ExecutorService service = Executors.newFixedThreadPool(10); ...

  9. Spring之事务底层原理源码解析

    文章目录 一.`@EnableTransactionManagement`工作原理 二.Spring事务基本执行原理 三.Spring事务详细执行流程 四.Spring事务传播机制 五.Spring事 ...

最新文章

  1. WebAssembly Studio:Mozilla提供的WASM工具
  2. Node安装node-sass总是下载超时问题解决
  3. JavaScript精进篇
  4. python列表、元组、字典和集合的算法时间_27.Python列表(list)、元组(tuple)、字典(dict)和集合(set)详解...
  5. 编程方法学6:操作符
  6. ar ebs 销售订单关闭_有关销售订单的工作流(workflow)定义和处理方式
  7. SpringCloud微服务:Eureka组件之服务注册与发现
  8. 【免费】某机构最新3980元机器学习/大数据课程高速下载,限量200份
  9. 未来经济会发展到什么程度?
  10. 最新仿7881游戏装备网虚拟物品交易源码+修复版
  11. 微信小程序开发(创建项目介绍)
  12. 华氏度和摄氏度转换for-Python
  13. 项目管理工具project软件学习(二) - 自定义日历【标准+节假日】
  14. mybatis批量删除 java_Mybatis批量删除数据操作方法
  15. 一体化低压伺服电机在注塑机械手上的应用
  16. c语言跳马思路,跳马问题 - 日拱一卒的个人空间 - OSCHINA - 中文开源技术交流社区...
  17. 恢复系统自带的office软件
  18. C语言学习1——第一、二、三章学习记录
  19. ps 仿章工具的使用
  20. java 过期数据_TTL过期的数据包丢失

热门文章

  1. 方维分享怎样修改数据库配置
  2. 微信公众号运营错误的四个方式
  3. 一文读懂设计模式--适配器模式
  4. 概率,递推,找规律,高精度(FXTZ II,hdu 4043)
  5. sdk是什么意思_学好前端的6点建议,企业需要什么样的Web前端人才?
  6. R中gRain包的加载和使用
  7. 21天设计Arm芯片|移知公开课
  8. Java工程师学习指南(2019最新版)
  9. 奥的斯电梯服务器自动呼梯,奥的斯外呼电梯控制系统说明
  10. MTK keypad调试,扩张键盘IC AW9523