spdk探秘-----块设备开发指导

2023-11-04

这里的块设备是一种存储设备,它支持在固定大小的块中读写数据。这些块通常是512或4096字节。这些设备可能是软件中的逻辑结构,或者对应于像NVMe ssd这样的物理设备。

通用库的公共头文件是bdev.h,它是与任何类型的块设备交互所需的全部API。

除了为所有块设备提供一个通用的抽象之外,bdev层还提供了许多有用的特性:

1、I/O请求自动排队,以响应队列满或内存不足的情况

2、热删除支持,即使有I/O流量时。

3、I/O统计信息,如带宽和延迟

4、设备重启和I/O超时跟踪

struct spdk_bdev(bdev)表示一个通用块设备。struct spdk_bdev_desc,称为描述符,表示给定块设备的句柄。描述符用于建立和跟踪使用底层块设备的权限,非常文件描述符。对块设备的请求是异步的,并由spdk_bdev_io对象表示。请求必须在关联的I/O通道上提交。将从消息传递和并发两个方面阐述了I/O通道的设计和动机。

Bdevs可以分层,这样一些Bdevs通过将请求路由到其他Bdevs来服务I/O。这可以用于实现缓存、RAID、逻辑卷管理等。将I/O路由到其他bdev的bdev通常称为虚拟bdev,或简称vbdev。

初始化库

bdev层依赖于头文件include/spdk/thread.h提供的通用消息传递基础设施。最重要的是,只能通过spdk_thread_create()分配的线程才能调用bdev库。

在分配的线程中,可以通过调用spdk_bdev_initialize()来初始化bdev库,这是一个异步操作。在调用完成回调之前,不能调用其他bdev库函数。相反的要卸载bdev库,可以调用spdk_bdev_finish()。

发现块设备

所有块设备都有一个简单的字符串名称。在任何时候都可以通过调用spdk_bdev_get_by_name()获得指向设备对象的指针,也可以使用spdk_bdev_first()和spdk_bdev_next()迭代整个bdevs集合。

一些块设备也可能被赋予别名,这也是字符串名称。别名的行为类似符号链接——它们可以与真名互换使用,以查找块设备。

块设备准备

为了向块设备发送I/O请求,必须首先通过调用spdk_bdev_open_ext()来打开它。这将返回一个描述符。多个用户可能同时打开一个bdev,用户之间读写的协调必须由bdev层之上的机制来处理。如果虚拟bdev模块claimed了bdevA,那么使用写权限打开bdevA可能会失败。因为虚拟bdev模块实现像RAID或逻辑卷管理这样的逻辑,并将它们的I/O转发给较低级别的bdevA,因此它们将这些较低级别的bdevA标记为claimed的,以防止外部用户发出写操作。

当块设备打开时,必须提供回调和上下文,当bdev触发异步事件(如bdev删除)时,将使用适当的spdk_bdev_event_type enum作为参数调用它们。例如,当NVMe SSD热拔时,将对物理NVMe SSD支持的bdev的每个打开的描述符调用回调。在这种情况下,可以将回调看作是关闭描述符的请求,以便释放其他内存。当描述符存打开时不能卸载bdev,因此需要提供回调。

当用户使用完描述符时,他们可以通过调用spdk_bdev_close()来释放它。

描述符可以同时传递给多个线程并在多个线程使用。但是,对于每个线程,必须通过调用spdk_bdev_get_io_channel()来获得一个单独的I/O通道。这将为每个线程分配必要的资源,以便在不获取锁的情况下向bdev提交I/O请求。要释放一个通道,请调用spdk_put_io_channel()。在所有相关联的通道被销毁之前,描述符不能被关闭。

发送I / O

一旦获得了描述符和通道,就可以通过调用各种I/O提交函数(如spdk_bdev_read())来发送I/O。这些调用都接受一个回调函数作为参数,在后面会使用spdk_bdev_io对象的句柄来调用该回调函数。为了响应这个完成,用户必须调用spdk_bdev_free_io()来释放资源。在这个回调中,用户还可以使用函数spdk_bdev_io_get_nvme_status()和spdk_bdev_io_get_scsi_status()来获取他们选择的格式的错误信息。

I/O提交是通过调用spdk_bdev_read()或spdk_bdev_write()等函数来执行的。存储需要写入device的数据的内存必须通过spdk_dma_malloc()或它的变体来分配。在用户空间的直接内存访问(DMA)中会介绍为什么必须使用DMA来分配。在一般情况下,内存中的数据将被直接传输到块设备。这个过程零拷贝。

所有的I/O提交函数都是异步的和非阻塞的。它们不会因为任何原因阻塞或停止线程。但是,I/O提交函数可能有两种失败。首先,它们可能会立即失败并返回一个错误代码。在这种情况下,提供的回调将不会被调用。其次,它们可能异步失败。在这种情况下,相关的spdk_bdev_io将被传递给回调函数,它将报告错误信息。

有些I/O请求类型是可选的,可能不被给定的bdev支持。要查询bdev支持的I/O请求类型,请调用spdk_bdev_io_type_supported()。

重置块设备

为了处理意外的故障条件,bdev库提供了一种通过调用spdk_bdev_reset()来重置的机制。这将向bdev存在I/O通道的每个其他线程传递消息,暂停它,然后将一个复位请求转发给底层bdev模块,并等待完成。完成后,I/O通道将恢复,重置将完成。bdev模块中的特定行为是特定于模块的。例如,NVMe设备将删除所有队列对,执行一个NVMe重置,然后重新创建队列对并继续。最重要的是,无论设备类型是什么,对块设备的所有I/O都将在重置完成之前完成。

编写自定义块设备模块

这个指南是为编写自己的块设备模块以与SPDK的bdev层集成的开发人员准备的。

在SPDK中,块设备模块相当于传统操作系统中的设备驱动程序。模块提供了一组函数指针,用于服务块设备I/O请求。SPDK提供了许多块设备模块,包括NVMe、RAM-disk和Ceph RBD。但是,有些用户希望编写自己的存储,以便与自定义硬件或现有的存储软件堆栈交互。本指南旨在准确地演示如何编写模块。

创建新模块

块设备模块现在位于lib/bdev的子目录中。目前还不能将bdev模块的代码放在其他地方。要创建模块,添加一个带有单个C文件和Makefile的新目录。一个很好的途径是复制现有的“null”bdev模块。

bdev模块将与之交互的主要接口在include/spdk/bdev_module.h中。在这个头文件中定义了一个宏SPDK_BDEV_MODULE_REGISTER来注册一个新的bdev模块。这个宏采用一个指针spdk_bdev_module结构作为参数,该结构用于注册新的bdev模块。spdk_bdev_module结构描述了模块属性初始化(module_init)和卸载(module_fini)函数。可以查看struct spdk_bdev_module的文档了解更多细节。

创建Bdevs

通过调用spdk_bdev_register()在模块中创建新的bdev。新模块必须分配struct spdk_bdev,并初始化,并将它传递给寄存器调用。要初始化的最重要的字段是fn_table,它指向这个数据结构:

/*
* Function table for a block device backend.
*
* The backend block device function table provides a set of APIs to allow
* communication with a backend. The main commands are read/write API
* calls for I/O via submit_request.
*/
struct spdk_bdev_fn_table {
/* Destroy the backend block device object */
int (*destruct)(void *ctx);
/* Process the IO. */
void (*submit_request)(struct spdk_io_channel *ch, struct spdk_bdev_io *);
/* Check if the block device supports a specific I/O type. */
bool (*io_type_supported)(void *ctx, enum spdk_bdev_io_type);
/* Get an I/O channel for the specific bdev for the calling thread. */
struct spdk_io_channel *(*get_io_channel)(void *ctx);
/*
* Output driver-specific configuration to a JSON stream. Optional - may be NULL.
*
* The JSON write context will be initialized with an open object, so the bdev
* driver should write a name (based on the driver name) followed by a JSON value
* (most likely another nested object).
*/
int (*dump_config_json)(void *ctx, struct spdk_json_write_ctx *w);
/* Get spin-time per I/O channel in microseconds.
* Optional - may be NULL.
*/
uint64_t (*get_spin_time)(struct spdk_io_channel *ch);
};

bdev模块必须实现这些函数回调。

当系统不再需要该设备时,调用析构函数来拆除该设备。destruct所做的是由模块决定:可能只是释放内存,也可能是关闭一块硬件。

io_type_supported函数返回是否支持特定的I/O类型。可用的I/O类型有:

enum spdk_bdev_io_type {
SPDK_BDEV_IO_TYPE_INVALID = 0,
SPDK_BDEV_IO_TYPE_READ,
SPDK_BDEV_IO_TYPE_WRITE,
SPDK_BDEV_IO_TYPE_UNMAP,
SPDK_BDEV_IO_TYPE_FLUSH,
SPDK_BDEV_IO_TYPE_RESET,
SPDK_BDEV_IO_TYPE_NVME_ADMIN,
SPDK_BDEV_IO_TYPE_NVME_IO,
SPDK_BDEV_IO_TYPE_NVME_IO_MD,
SPDK_BDEV_IO_TYPE_WRITE_ZEROES,
};

对于最简单的bdev模块,只需要SPDK_BDEV_IO_TYPE_READ和SPDK_BDEV_IO_TYPE_WRITE。SPDK_BDEV_IO_TYPE_UNMAP通常被称为“trim”或“deallocate”,它是一个将一组块标记为不再包含有效数据的请求。SPDK_BDEV_IO_TYPE_FLUSH请求使之前完成的所有写操作具有持久性,许多设备不需要flush。SPDK_BDEV_IO_TYPE_WRITE_ZEROES就像常规的写操作,但是不提供数据缓冲区(它只包含所有的0)。如果不支持它,一般的bdev代码可以通过发送常规的写请求来模拟它。

SPDK_BDEV_IO_TYPE_RESET是一个中止所有I/O并将底层设备返回到初始状态的请求。在以某种方式完成所有I/O之前,不要完成重置请求。

SPDK_BDEV_IO_TYPE_NVME_ADMIN、SPDK_BDEV_IO_TYPE_NVME_IO_MD和SPDK_BDEV_IO_TYPE_NVME_IO_MD都是通过SPDK bdev层传递原始NVMe命令的机制。它们是严格可选的,可能只有在后备存储设备能够处理NVMe命令时才有意义。

get_io_channel函数应该返回一个I/O通道。通用bdev层将每个线程调用一次get_io_channel,缓存结果,并将结果传递给submit_request。它将为调用submit_request的线程使用相应的通道。

submit_request函数被调用来实际地向块设备提交I/O请求。I/O请求完成后,模块必须调用spdk_bdev_io_complete()。I/O不必在submit_request的调用上下文中完成。

创建虚拟Bdevs

如果块设备A通过将I/O路由到其他块设备B来处理I/O请求,则认为A是虚拟的。典型的例子是实现RAID的bdev模块就是个虚拟模块。虚拟bdev的创建方式与常规bdev相同,但需要额外的一个步骤。虚拟模块可以使用spdk_bdev_get_by_name()查找它希望将I/O路由到的底层bdevs,其中字符串名称由用户在配置文件中或通过RPC提供。然后通过打开底层bdev获取描述符,并为bdev创建I/O通道(可能是对get_io_channel回调的响应),模块可以正常运行。最后一步是让虚拟模块用其底层块设备描述符来调用spdk_bdev_module_claim_bdev(),表明该虚拟模块正在使用底层的bdev。这将防止其他用户在该底层模块上打开具有写权限的描述符。这有效地将描述符“提升”为写独占,并且这是一个仅对bdev模块可用的操作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

spdk探秘-----块设备开发指导 的相关文章

  • spdk探秘-----vhost 、ISCSI、 NVMe-oF Target

    vhost target 这里我们主要介绍用SPDK vhost target来加速虚拟机中的I O 在介绍这个加速方案之前 我们先看看主流的I O设备虚拟化的方案 纯软件模拟 完全利用软件模拟出一些设备给虚拟机使用 主要的工作可以在Sim
  • 静态地址重定位 与 动态地址重定位

    静态地址重定位 即在程序装入内存的过程中完成 是指在程序开始运行前 程序中的各个地址有关的项均已完成重定位 地址变换通常是在装入时一次完成的 以后不再改变 故成为静态重定位 优点 无需硬件支持 缺点 1 程序重定位之后就不能在内存中搬动了
  • dlmalloc解析连载一

    dlmalloc是目前一个十分流行的内存分配器 其由Doug Lea 主页为 http gee cs oswego edu 从1987年开始编写 到目前为止 最新版本为2 8 3 可以从 ftp g oswego edu pub misc
  • 非printf形式打印各种数据类型的十六进制和二进制

    转载请标明是引用于 http blog csdn net chenyujing1234 欢迎大家提出意见 一起讨论 一 源码实现 为了适配各种数据类型 且可以通过sizeof得到此类型的大小 所以这里采用模板形式开发 以下是实现此功能的源码
  • fileinput组件将文件上传到服务器的路径编写方法以及Tomcat的配置

    1 先配置文件上传时Tomcat的虚拟路径 system file read location http localhost 8080 fileserversavepath 这是访问图片的地址 system file write locat
  • 数据库中存储过程、函数、触发器的区别

    存储过程 函数 触发器的区别 比较项目 存储过程 函数 是否有返回值 可以有 也可以没有 必须有且只有一个 是否可以单独执行 可以 必须通过execute执行 SQL语句 DML或SELECT 可否调用 不可以 可以 且可以位于FROM关键
  • 数据库的安全性、完整性、并发控制和恢复

    数据库的安全性 完整性 并发控制和恢复from http bbs chinaunix net viewthread php tid 188100 为了保证数据库数据的安全可靠性和正确有效 DBMS必须提供统一的数据保护功能 数据保护也为数据
  • 浅谈RAID写惩罚(Write Penalty)与解决方案闪存荷尔蒙(FlashHormone)

    浅谈RAID写惩罚 Write Penalty 与解决方案闪存荷尔蒙 FlashHormone 介绍 通常在讨论不同RAID保护类型的性能的时候 结论都会是RAID 1提供比较好的读写性能 RAID 5读性能不错 但是写入性能就不如RAID
  • 日增30-40亿数据量的数据库

    author skate time 2010 08 13 前几天和个朋友聊天 他说他有每天30 40亿条数据量的数据库如何规划与优化 简单了解需求是这30 40亿数据是每天采集的 然后同时还对这些采集的数据进行分析挖掘 对于这么大量的数据量
  • leveldb深度剖析-查询流程

    至此 将插入流程以及压缩流程都已介绍完毕了 本篇主要介绍查询流程 一 查询流程 首先来看一下查询接口具体实现内容 查询 param options 查询选项 param key 查询key param value 输出参数 如果找到则赋值给
  • 在离线渲染器中应用MERL BRDF

    BRDF Bidirectional Reflection Density Function 即出射光线的radiance和入射光线的irradiance的比值 在图形学中被用来描述物体的表面反射属性 BRDF的值一般来说由几个参数决定 入
  • fatal error C1083: 无法打开预编译头文件:“Debug\opencv.pch”: No such file or directory

    步骤 方法右键点击你创建的项目 选择 属性标签 点击属性 弹出 项目属性页 在左侧找到以下位置 配置属性 gt C C gt 预编译头 并选择它 在右边的菜单中选择 创建 使用预编译头 中的 不使用预编译头文件 点击 确定 按钮退出即可原因
  • java中的基本数据类型和引用数据类型以及它们的存储方式堆内存和栈内存

    一直对java中的基本数据类型和引用数据类型之间的关系搞不太清楚 今天做leetcode的一道题目 总算弄清楚了关系 写下来和大家一起分享一下 一 基本数据类型 数据类型在计算机语言里面 是对内存位置的一个抽象表达方式 可以理解为针对内存的
  • Raneto

    Raneto Raneto是一个采用Node js开发的免费开源知识库平台 它使用Markdown文件来存储知识库 Raneto也可以叫作一个 静态网站生成器 因为它不需要数据库 你的所有内容都存储在 Markdown md 文件中 整个知
  • Amazon——Elastic Compute Cloud(EC2)

    云计算 教材试读 弹性计算云EC2 4 3 弹性计算云EC2 亚马逊弹性计算云服务 Elastic Compute Cloud EC2 是亚马逊提供的云计算环境的基本平台 网络数据流的流向非常复杂 企业和个人的网络平台所需的计算能力也随着这
  • 浅谈Linux的文件系统, 新增XFS.Ext3.GFS.ReiserFS.JFS相关知识

    如果您是一位新手 也许 您还不知道如何把文件从Windows拷贝到 Linux上吧 下面 我们将说明Unix文件系统以及mount的工作过程 然后再比较详细地讨论 mount的使用和有关选项 如果您已经了解Unix文件系统是如何工作的 那么
  • 阿里云配置MINIO图床

    阿里云 ECS服务器 CENTOS7系统部署MINIO图床 1 下载MINIO的二进制文件 注 阿里云ECS网速过慢 但可以接受 wget https dl minio io server minio release linux amd64
  • 内存的堆分配和栈分配 & 字符数组,字符指针,Sizeof总结

    程序占用的内存分为几个部分 各个部分起什么作用 字符数组 字符指针在实现上有什么区别等等 本文对此做了详细阐述 特转载于此 供大家学习参考之用 一个由C C 编译的程序占用的内存分为以下几个部分 1 栈区 stack 由编译器自动分配释放
  • ostream_iterator详细解析

    ostream iterator属于I O流STL适配器 用于获取一个元素 同时保存在缓冲器中 可以供Cout输出 如果把cout看做成一个对象 那么在Cout对象当中存在一片用于数据存储的区域 ostream iterator在STL中一
  • 函数getopt(),及其参数optind

    getopt被用来解析命令行选项参数 转载地址 http hi baidu com xlt1888 blog item 703148383008492670cf6c2d html include

随机推荐

  • 「Python 基础」异步 I/O 编程

    I O 密集型应用程序大大提升系统多任务处理能力 异步 I O 模型 一个消息循环 主线程在消息循环中不断重复 读取消息 处理消息 获取线程池 loop get event loop while True 接收事件消息 event loop
  • R语言实现个人信用风险评估(数据科学导引)

    1 案例背景 在很多国家 政府机构会密切监控贷款业务银行需要明确解释申请者的贷款申请被拒绝或者批准的原因 这种可解释性对于贷款申请者也是很重要的 在贷款申请被银行拒绝时 申请者需要知道为什么自己的信用级别不符合银行的要求 通过构建自动化的信
  • es脚本 实现字段之间进行比对

    script script source ctx source extra test lang painless query term user kimchy java BoolQueryBuilder boolQueryBuilder Q
  • 详解比较古怪的字符串拆分函数:strtok函数

    对于字符串中的标点符号 字符呀 如何能够当作分隔符来对原字符串进行分割呢 分割出来的为一段一段的字符 因此 在这里面就用到了 本文 比较古怪的字符串拆分函数 strtok函数 假设 对于这个字符串 woaini wangyijun com
  • 3.4 三级指针

    char p NULL 注 1 可以通过三级指针间接的改变二级指针的指向 2 p表示 三级指针指向的二级指针中保存的内存地址 3 三级指针做函数参数时 主调函数需要传2级指针的地址 4 n级指针可以间接修改n 1级指针的指向 下面的例子通过
  • @Component注解的作用

    Spring自带的 Component注解及扩展 Component 定义Spring管理Bean 也就是将标注 Component注解的类交由spring管理 AspectJ风格的切面可以通过 Compenent注解标识其为Spring管
  • VMware10上新建虚拟机步骤图解 + 安装Centos 7(64位) 系统

    原文 https blog csdn net hometing218 article details 79486172
  • RTKlib单点定位-部分思考

    塔奇克敲代码 博主的博客 RTKLIB源码解析 单点定位 将单点定位部分整理成函数小卡片 为我理解RTKlib提供了很大的帮助 他在单点定位部分列出了一些疑惑 在此我记录下我对部分疑惑的理解 文章目录 1 pntpos函数 2 satpos
  • vcs+verdi,以及Makefile注意点

    Makefile 命令行之前是以Tab开头的不然会报错 gvim里面强制输入tab 使用Ctr v i 直接使用tab键可能输入不成功 注释用 下面是makefile内容 L8 可选debug debug pp debug pp 使能ucl
  • 树莓派通信协议——MQTT的安装及使用

    MQTT是一种基于TCP IP协议栈构建的异步通信协议 是一种轻量级的发布 订阅信息传输协议 基于topic订阅关系的发布和推送 在实践中可空间上 将消息发送者和接受者分离 可以再不可靠的网络环境中进行扩展 适用于设备硬件存储空间有限或网络
  • 【满分】【华为OD机试真题2023 JAVA&JS】木板

    华为OD机试真题 2023年度机试题库全覆盖 刷题指南点这里 木板 时间限制 1s 空间限制 256MB 限定语言 不限 题目描述 小明有n块木板 第i 1 i n 块木板的长度为ai 小明买了一块长度为m的木料 这块木料可以切割成任意块
  • R手册(NLP)--wordcloud2

    文章目录 wordlcoud2函数 letterCloud函数 shiny支持 wordcloud2 R interface to wordcloud for data visualization Wordcloud2主要包括两个函数 wo
  • ‘XXX’ is already defined @typescript-eslint/no-redeclare 警告 问题解决

    上文React Typescript项目环境中搭建并使用redux环境 结束是 其实不算完全写完吧 还会留下一个警告 这个报错 好像是说 这两个值已经定义过了 可能很多人 会觉得小问题 但你会发现 无论你名字怎么改都会一直 带着你新的名字继
  • Jenkins+sonarqube+sonar-scanner扫描java文件乱码怎么解决?

    在jenkins上扫描java项目 提示乱码 有没有知道怎么解决啊 WARN Invalid character encountered in file root jenkins workspace sonar scanner rrdl t
  • Element中table组件根据属性合并行数据

    在实际开发中 要求使用elementUI的table组件对表格数据上下行相邻相同的数据进行合并 在elem官网上查看到是有对应的组件和合并方法
  • apache doris和StarRocks的区别

    Apache Doris是一个分布式的列式存储系统 它的设计目标是提供大规模数据处理的可靠性和高性能 Doris采用了集群方式 通过将数据分布在多个机器上进行处理来提高性能 并提供了SQL查询接口方便用户使用 StarRocks是一个分布式
  • 【Windows10下MySQL(8.0)免费版安装与卸载图文教程】

    WIN10安装MYSQL8 0图文教程 Windows10下MySQL 8 0版本 免费版安装与卸载 一 下载 二 解压 三 创建配置文件以及存放数据的文件夹 四 配置系统环境变量 五 以管理员打开命令窗口 Win键 X键 六 安装 七 连
  • idea access数据库连接_几款好用的数据库,选择适合自己的那盘菜

    先来说一下什么是数据库 通俗讲 它就像物品仓库一样 可以存储电子数据 但是不同的是 它是以一定数据结构进行存储的 用户也可以对其中的数据通过一定的语言方式进行新增 查询 更新 删除等操作 大家接触的比较多的是Excel数据表格 实际它只能称
  • Elasticsearch-head插件安装教程

    目录标题 前言 一 安装node 二 安装Elasticsearch head插件 1 上传压缩包到 opt es路径下去 2 解压安装包 3 192 168 43 10机器修改Gruntfile js 4 192 168 43 10机器修
  • spdk探秘-----块设备开发指导

    这里的块设备是一种存储设备 它支持在固定大小的块中读写数据 这些块通常是512或4096字节 这些设备可能是软件中的逻辑结构 或者对应于像NVMe ssd这样的物理设备 通用库的公共头文件是bdev h 它是与任何类型的块设备交互所需的全部