首页/目录/全部文章

全部文章

八个专题的源码、算法与协议笔记都在这里。

笔记列表

Zephyr 设备模型、驱动与电源管理

Zephyr 设备模型、驱动与电源管理

1. struct device

设备模型公共定义在 include/zephyr/device.h。每个设备实例由静态 struct device 表示,主要关系:

1
2
3
4
5
6
7
8
struct device
├─ name
├─ config → 编译期常量、寄存器地址、DT规格
├─ data → 运行时状态、锁、callback、DMA状态
├─ api → class-specific vtable
├─ state → init result / initialized
├─ pm → device PM
└─ handles → required/supported/injected dependencies

设备对象不是动态驱动注册表;绝大部分在编译和链接期生成。

2. 设备定义宏

非 DTS 设备:

1
2
DEVICE_DEFINE(dev_id, name, init_fn, pm,
data, config, level, prio, api);

DTS 设备:

1
2
3
4
DEVICE_DT_DEFINE(node_id, init_fn, pm,
data, config, level, prio, api);

DEVICE_DT_INST_DEFINE(inst, ...);

宏内部完成:

  1. 定义 device state;
  2. 定义依赖 handle 数据;
  3. 创建 struct device
  4. 创建 struct init_entry
  5. 放入按 init level/priority 排序的 linker section。

3. Driver class API

每一类设备有统一 vtable,例如 GPIO:

1
2
3
4
5
6
7
struct gpio_driver_api
├─ pin_configure
├─ port_get_raw
├─ port_set_masked_raw
├─ port_toggle_bits
├─ pin_interrupt_configure
└─ manage_callback

public inline API:

1
2
3
gpio_pin_configure_dt()
→ gpio_pin_configure()
→ api->pin_configure(dev, pin, flags)

好处:

  • 应用与 vendor driver 解耦;
  • API 调用通常只有一次 indirect call;
  • class 可提供统一参数检查和 syscall verifier;
  • driver data/config 保持私有。

4. DTS 驱动实例化

典型驱动结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#define DT_DRV_COMPAT vendor_peripheral

struct drv_config {
DEVICE_MMIO_ROM;
struct clock_control_dt_spec clock;
const struct pinctrl_dev_config *pcfg;
};

struct drv_data {
struct k_mutex lock;
};

static int drv_init(const struct device *dev) { ... }
static const struct api drv_api = { ... };

#define DRV_INIT(inst) \
PINCTRL_DT_INST_DEFINE(inst); \
static struct drv_data data_##inst; \
static const struct drv_config config_##inst = { ... }; \
DEVICE_DT_INST_DEFINE(inst, drv_init, PM_DEVICE_DT_INST_GET(inst), \
&data_##inst, &config_##inst, \
POST_KERNEL, CONFIG_DRV_INIT_PRIORITY, &drv_api);

DT_INST_FOREACH_STATUS_OKAY(DRV_INIT)

只有 status okay 且对应驱动源码已由 Kconfig/CMake编译时,实例才存在。

5. 获取设备

编译期获取:

1
const struct device *dev = DEVICE_DT_GET(node);

它直接取得全局符号地址,没有运行时查找。之后必须:

1
2
3
if (!device_is_ready(dev)) {
return -ENODEV;
}

常见 spec:

1
2
static const struct gpio_dt_spec led =
GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);

spec ready 检查:

1
2
3
gpio_is_ready_dt(&led)
i2c_is_ready_dt(&spec)
spi_is_ready_dt(&spec)

device_get_binding(name) 进行运行时名称查找,适合 shell/动态配置;普通静态应用优先 DEVICE_DT_GET

6. Device init

启动时 z_sys_init_run_level() 遇到 device entry:

  1. 检查 deferred init;
  2. 调用 device init function;
  3. 保存 init result;
  4. 标记 initialized;
  5. device_is_ready() 只有在 initialized 且 result 为 0 时为 true。

依赖 controller 必须比 child 更早初始化。常用 priority:

  • interrupt controller、clock、pinctrl:PRE_KERNEL;
  • bus controller:PRE_KERNEL/POST_KERNEL;
  • bus child sensor:POST_KERNEL;
  • application service:APPLICATION。

仅调整 priority 不能修复循环依赖。

7. Device dependency handles

生成系统根据 DTS phandle/dependency ordinal 建立:

  • required devices;
  • supported devices;
  • injected dependencies。

handle 使用 int16_t,比 pointer list 紧凑。多阶段链接读取预链接设备信息后生成最终 dependency arrays。

用途:

  • 初始化依赖理解;
  • device PM suspend/resume 排序;
  • shell/debug dependency 查询。

8. Deferred initialization

节点可设置 zephyr,deferred-init。此时启动阶段不自动 init,应用稍后调用 device init API。

适合:

  • 供电域未开启;
  • 外部器件需业务控制的 reset sequence;
  • 减少启动时间。

使用方必须处理并发首次初始化和依赖设备状态。

9. GPIO

公共 API:include/zephyr/drivers/gpio.h

重点:

  • raw 与 logical API;
  • active-low 由 flags 转换;
  • pin/port 操作;
  • interrupt edge/level;
  • gpio_callback list;
  • gpio_dt_spec

callback 常在 ISR 上下文执行,复杂处理应提交 work。

10. I2C / SPI

I2C

include/zephyr/drivers/i2c.h

  • controller configure;
  • i2c_transfer() messages;
  • write/read/write_read;
  • target mode;
  • i2c_dt_spec

device address 来自 child reg。驱动应区分 7-bit/10-bit 和 restart/stop flags。

SPI

include/zephyr/drivers/spi.h

  • spi_config:frequency、operation、slave、CS;
  • spi_buf_set
  • transceive/read/write;
  • async;
  • spi_dt_spec

CS 可以由 controller hardware 或 cs-gpios 管理。buffer 生命周期必须覆盖异步传输。

11. UART

include/zephyr/drivers/uart.h 提供三层 API:

  • polling;
  • interrupt-driven FIFO;
  • async DMA/event API。

console、shell、logging backend、mcumgr 可能同时竞争 UART。需要明确谁拥有 RX callback、是否共享 TX,以及 panic 阶段是否可轮询输出。

12. Flash

include/zephyr/drivers/flash.h

  • read/write/erase;
  • page layout;
  • write block size;
  • erase value;
  • protection。

上层 subsys/storage/flash_map 把 fixed-partitions 转成 flash_area。文件系统、settings、DFU 和 MCUboot 通常使用 flash map,而非硬编码地址。

Flash 写入注意:

  • 地址/长度写对齐;
  • erase block 边界;
  • 从 erased value 到 programmed value 的位变化限制;
  • erase/write 期间 XIP 冲突;
  • cache 一致性;
  • watchdog 和最长阻塞时间。

13. Clock 与 Pinctrl

Clock control

设备 config 保存 clock_control_subsys_t/DT spec,init 时:

1
2
3
clock_control_on
clock_control_set_rate
clock_control_get_rate

SoC clock tree 通常在 PRE_KERNEL 初始化。

Pinctrl

DT 定义 pinctrl-0pinctrl-1 等 state。驱动使用:

1
2
3
PINCTRL_DT_INST_DEFINE
PINCTRL_DT_INST_DEV_CONFIG_GET
pinctrl_apply_state

default/sleep state 可与 PM 联动。pinmux 冲突应在 DTS 设计阶段解决。

14. DMA

DMA API 描述:

  • channel;
  • direction;
  • source/destination data size;
  • burst;
  • block chain;
  • callback。

驱动使用 DMA 时必须考虑:

  • cache clean/invalidate;
  • buffer alignment;
  • memory region 是否 DMA 可访问;
  • callback ISR 上下文;
  • abort race;
  • peripheral request mapping。

15. Interrupt 连接

静态连接:

1
2
IRQ_CONNECT(irq, priority, isr, arg, flags);
irq_enable(irq);

DTS 驱动常使用:

1
2
DT_INST_IRQN(inst)
DT_INST_IRQ(inst, priority)

构建系统可能生成 software ISR table。direct ISR 可降低开销,但功能约束更多。zero-latency IRQ 绕过部分内核路径,ISR 不得调用普通内核 API。

16. Device PM

公共 API:

  • include/zephyr/pm/device.h
  • subsys/pm/device.c
  • subsys/pm/device_runtime.c

驱动通过:

1
PM_DEVICE_DT_INST_DEFINE(inst, action_cb);

实现 action:

  • suspend;
  • resume;
  • turn off/on;
  • low power。

System-managed PM

系统进入低功耗前按依赖顺序 suspend devices,唤醒后逆序 resume。

Runtime PM

使用计数控制单设备:

1
2
pm_device_runtime_get()
pm_device_runtime_put()

首次 get 恢复设备,最后 put 可 autosuspend。调用者必须保持 get/put 配对,driver 需处理与 system PM 的交互。

17. 驱动移植步骤

  1. 确认 class API,避免自造重复接口;
  2. 编写 binding;
  3. DTS 添加节点和依赖;
  4. Kconfig 定义 feature/vendor symbol;
  5. CMake 按 Kconfig 加源文件;
  6. 定义 config/data/api;
  7. DT_INST_FOREACH_STATUS_OKAY 实例化;
  8. 在 init 中检查 clock/pinctrl/bus;
  9. 实现 PM 和 error unwind;
  10. 添加 ztest、emulator 或测试 board;
  11. 检查多实例、disabled 节点、无 DT 实例时可编译;
  12. 测试 ISR、timeout、并发和异常恢复。

18. 常见驱动问题

  • DTS compatible 正确但 Kconfig 未开:无 device symbol;
  • controller 未 ready:child init 失败;
  • init priority 过早:使用了尚不可用的 kernel API;
  • data 错放为 const 或 config 可变;
  • callback 中阻塞;
  • DMA buffer 在 stack 上提前失效;
  • runtime PM 引用泄漏;
  • 未处理 active-low;
  • 外设 reset/clock 顺序错误;
  • DTS pinctrl state 与 board 实际连线不符。

Zephyr 主要子系统与公共库

Zephyr 主要子系统与公共库

1. Logging

路径:

1
2
subsys/logging/
include/zephyr/logging/

模块注册:

1
2
LOG_MODULE_REGISTER(name, CONFIG_NAME_LOG_LEVEL);
LOG_INF("value=%d", value);

架构:

1
2
3
4
5
6
call site
→ compile-time level/filter
→ log message/package
→ immediate 或 deferred processing
→ runtime filter
→ backend (UART/RTT/USB/FS/network...)

Immediate

调用线程直接格式化并输出:

  • 实现简单;
  • crash 前更容易看到日志;
  • ISR 和实时路径延迟大;
  • backend 可能阻塞。

Deferred

消息先进入 buffer,由 logging thread 处理:

  • 减少调用点延迟;
  • 需要 buffer 和线程 stack;
  • buffer 满时可能 drop/block;
  • reset 前未处理日志可能丢失。

生产配置需要在可观测性、RAM 和 worst-case latency 间权衡。

2. Shell

路径:

1
2
subsys/shell/
include/zephyr/shell/

command 通过 linker section 静态注册:

1
SHELL_CMD_REGISTER(name, subcmd, help, handler);

组成:

  • shell core/parser;
  • command tree;
  • backend(UART、RTT、Telnet、USB CDC 等);
  • history、wildcard、help、log integration;
  • subsystem commands。

handler 运行在 shell thread,长操作仍会阻塞 shell。危险生产命令应进行权限、状态和物理访问控制。

3. Settings

路径:

1
2
subsys/settings/
include/zephyr/settings/

逻辑:

1
2
3
settings subsystem
├─ handler tree: name/get/set/commit/export
└─ storage backend: NVS/FCB/file/custom

启动:

1
2
3
settings_subsys_init();
settings_register(&handler);
settings_load();

set 解析单项,commit 在整批 load 后应用依赖关系。升级时要考虑 schema version、默认值和向后兼容。

4. Storage 与 Flash map

subsys/storage/flash_map 把 DTS fixed-partitions 暴露为:

1
2
3
4
5
flash_area_open
flash_area_read
flash_area_write
flash_area_erase
flash_area_get_sectors

subsys/storage/stream 提供顺序写 abstraction,DFU/flash image writer 可在内部处理 block alignment 和 buffering。

常见持久化技术:

  • NVS:key-value,扇区轮转;
  • FCB:append-only entry;
  • littlefs:掉电安全文件系统;
  • settings over NVS/FCB/file。

5. 文件系统

路径:

1
2
subsys/fs/
include/zephyr/fs/

VFS API:

  • mount/unmount;
  • open/read/write/seek/close;
  • stat;
  • opendir/readdir;
  • truncate/unlink/rename。

后端包括 littlefs、FAT、ext2、NVS/FCB 等不同类别。文件系统工作线程/锁、block device、cache 和掉电策略取决于具体后端。

6. DFU

路径:

1
2
subsys/dfu/
include/zephyr/dfu/

主要组件:

  • flash image buffered writer;
  • image validation utilities;
  • MCUboot application API;
  • target/stream abstraction。

应用使用 MCUboot 时:

1
2
3
4
5
6
接收 image
→ flash_img_buffered_write 到 secondary
→ 校验 header/hash
→ boot_request_upgrade(TEST/PERM)
→ reboot
→ 新应用 boot_write_img_confirmed()

DFU 接收成功不等于镜像可信,最终信任由 bootloader 签名验证建立。

7. MCUmgr

路径:

1
2
subsys/mgmt/mcumgr/
include/zephyr/mgmt/mcumgr/

层次:

1
2
3
4
5
transport (UART/BLE/UDP/shell...)
→ SMP framing
→ CBOR decode
→ management group
→ command handler

常用 group:

  • OS;
  • image;
  • filesystem;
  • shell;
  • settings;
  • statistics。

server 与 client API 均存在。生产环境必须设计认证、授权和 transport 暴露范围;协议可达不代表命令应对所有调用者开放。

8. Network stack

路径:

1
2
subsys/net/
include/zephyr/net/

层次:

1
2
3
4
5
6
7
application
├─ BSD sockets / native net_context
├─ HTTP/MQTT/CoAP/DNS/DHCP/SNTP/LwM2M/TLS
├─ TCP/UDP/IPv4/IPv6/6LoWPAN
├─ net_if / connection manager
├─ L2: Ethernet/Wi-Fi/802.15.4/PPP/CANbus/virtual
└─ network device driver

核心对象:

  • net_if:接口配置、地址、状态;
  • net_pkt:packet metadata;
  • net_buf:分片数据;
  • net_context/socket;
  • L2 API;
  • network management event。

packet/buffer 来自固定 pool,配置不足表现为运行时分配失败而非普通 heap 增长。应按最大并发连接、窗口、MTU 和协议层开销估算。

9. Socket 与 TLS

Zephyr 提供 POSIX 风格 sockets,可由 native stack 或 offload driver 实现。TLS credentials 子系统按 tag 管理:

  • CA;
  • client certificate;
  • private key;
  • PSK。

TLS heap/stack、entropy、证书时间校验和硬件 crypto 会显著影响资源与启动依赖。

10. USB

当前工作区没有 subsys/usbdrivers/usb* 实现目录,只残留部分 include/zephyr/drivers/usb/usb_c/ 头文件以及 DTS bindings。这是源码裁剪后的不完整接口面:

  • 不能仅通过打开上游 USB Kconfig 选项获得可用 stack;
  • 引用上游 USB sample 前应先恢复对应源码和依赖;
  • MCUboot 中存在 USB DFU 配置也不代表当前 Zephyr 主树已具备所需 USB device stack。

11. Power management

路径:

1
2
subsys/pm/
include/zephyr/pm/

系统 idle 时:

1
2
3
4
5
6
7
scheduler 无 runnable work
→ PM policy 选择 state
→ suspend devices
→ SoC/arch 进入低功耗
→ wake event
→ resume devices
→ 恢复调度

policy 可根据下一个 timeout、latency、residency 和 application constraint 决策。busy wait、永久 clock request、未配对 runtime PM 都会阻止低功耗。

12. Random 与 Entropy

subsys/random 提供:

  • non-cryptographic random;
  • cryptographically secure random;
  • entropy driver 接入。

安全用途必须调用 CSPRNG API,并确认:

  • hardware entropy device ready;
  • seed 不可预测;
  • 启动早期不会静默退化;
  • health test 和错误传播合理。

13. Debug、Coredump 与 Tracing

路径:

1
2
3
subsys/debug/
subsys/tracing/
subsys/timing/

能力包括:

  • coredump;
  • gdbstub;
  • symbol table;
  • thread analyzer;
  • stack usage;
  • CTF/SystemView tracing;
  • execution timing;
  • runtime stats。

这些功能改变 timing 和内存布局,性能结论应区分 instrumentation build 与 release build。

14. Zbus

subsys/zbus 提供 channel/observer 消息总线:

  • listener;
  • subscriber;
  • message subscriber;
  • runtime observer registration;
  • priority boost。

适合模块解耦和状态广播。channel message 是共享数据,需理解 publish copy、observer 执行上下文和阻塞行为。

15. RTIO

subsys/rtio 提供异步 I/O submission/completion queue:

  • SQE/CQE;
  • executor;
  • chained operations;
  • sensor/I2C/SPI 等适配。

目标是减少逐次同步调用开销,支持批量和异步 pipeline。buffer 生命周期和 completion 消费是正确性的关键。

16. IPC

subsys/ipc 包括:

  • IPC service;
  • RPMsg service;
  • backend abstraction;
  • endpoint bind/send/receive。

多核系统还依赖 shared memory、mailbox/IPI、cache coherence 和 remote firmware 生命周期。

17. Modem、Bluetooth 与协议组件

当前树存在 modem 相关实现,但没有 subsys/bluetooth 源码;include/zephyr/bluetooth/ 等残留头文件不能视为可链接实现。若从外部模块恢复复杂无线协议,它通常横跨:

  • controller/driver;
  • host stack;
  • network/L2;
  • settings 持久化;
  • workqueue/thread;
  • security/crypto;
  • management events。

排查不能只看单个 driver,需从 transport buffer、协议 thread priority、workqueue 和 memory pool 联合分析。

18. 公共库

lib/include/zephyr/sys/ 提供:

  • singly/doubly linked list;
  • ring buffer;
  • byte order;
  • CRC/hash/checksum;
  • JSON;
  • CBOR;
  • base64;
  • heap;
  • bitarray/bitmap;
  • math/time utilities;
  • POSIX compatibility;
  • minimal/newlib/picolibc integration。

公共库应优先于项目内重复实现,但需检查 Kconfig、线程安全、ISR safe 和内存分配行为。

19. 子系统集成原则

  1. 明确 thread/workqueue/context;
  2. 明确 buffer ownership;
  3. 明确 Kconfig 和 DTS 双重依赖;
  4. 对持久化数据做 schema 和掉电设计;
  5. 对外部管理接口做授权;
  6. 对 memory pool 做容量推导;
  7. 对 logging/debug 的实时性影响做 release 验证;
  8. 避免多个子系统争用同一 UART、flash partition 或 workqueue。

Zephyr 移植、测试与调试指南

Zephyr 移植、测试与调试指南

1. 新板级移植的分层

1
2
3
4
5
Architecture(通常复用)
└─ SoC support(尽量复用已有系列)
└─ Board DTS/defconfig
└─ Shield/overlay
└─ Application

若芯片已有 SoC 支持,新板通常只需:

  • board.yml
  • board .dts
  • <board>_defconfig
  • 可选 Kconfig、board.cmake、runner;
  • pinctrl/connector 描述。

不要把应用策略和大量功能选项塞进 board defconfig;board 默认值应主要描述板上始终存在的硬件能力。

2. Board 移植步骤

  1. 选择最接近的现有 board;
  2. 确认 SoC、flash、SRAM 容量;
  3. 创建 board.yml
  4. include 正确 SoC/参考板 .dtsi/.dts
  5. 配置 clocks;
  6. 配置 console 和 pinctrl;
  7. 启用必要 GPIO/interrupt controller;
  8. 添加 aliases/chosen;
  9. 定义 flash partitions;
  10. 设置最小 defconfig;
  11. 编译 samples/hello_world
  12. 再依次验证 GPIO、UART、I2C/SPI、flash、watchdog;
  13. 添加 board test/CI allowlist。

3. 新 SoC 移植

除 DTS 外还需:

  • SoC Kconfig;
  • CMake/runner/toolchain 选择;
  • reset/early init;
  • clock control;
  • interrupt controller;
  • system timer;
  • pinctrl;
  • memory map/linker;
  • cache/MPU/MMU;
  • reboot、poweroff、SMP;
  • HAL module glue。

优先把可复用寄存器操作放入 driver/HAL,把启动时序和 SoC 特有 glue 放入 soc/

4. Driver 验证层次

  1. 编译期:无实例/单实例/多实例;
  2. init:依赖设备和错误返回;
  3. API 正常路径;
  4. 参数边界;
  5. interrupt/callback;
  6. timeout/abort;
  7. 并发;
  8. runtime PM;
  9. suspend/resume;
  10. fault injection:bus NACK、DMA error、设备掉线。

能编译不代表 DTS binding、pinctrl 和真实硬件时序正确。

5. Ztest

框架路径:

1
2
subsys/testsuite/ztest/
include/zephyr/ztest.h

典型:

1
2
3
4
5
6
ZTEST_SUITE(suite, NULL, setup, before, after, teardown);

ZTEST(suite, test_case)
{
zassert_equal(actual, expected);
}

支持:

  • suite/test fixture;
  • parameterized/generated tests;
  • userspace tests;
  • expected fail/skip;
  • mock/fake;
  • rules 和 test phases。

测试不应依赖执行顺序,fixture 应恢复全局状态。

6. Twister

Twister 读取 testcase.yaml

1
2
3
4
5
6
tests:
subsystem.feature:
tags:
- feature
platform_allow:
- native_sim

作用:

  • 构建矩阵;
  • 运行 native/QEMU/硬件测试;
  • 过滤 architecture、toolchain、RAM/flash;
  • 解析 harness 输出;
  • 生成报告和覆盖率。

常用:

1
2
3
4
west twister -T tests/path
west twister -T tests/path -p native_sim
west twister -T tests/path --inline-logs
west twister -T tests/path --coverage

对 board/driver 代码至少增加 compile-only 和一个可执行场景。

7. Native simulation 与 QEMU

  • native_sim:把 Zephyr 编译为 host process,适合内核/协议/单元测试;
  • QEMU:模拟 CPU、interrupt 和部分外设,更接近目标架构;
  • unit testing architecture:隔离编译特定库;
  • hardware map:Twister 管理真实板。

硬件相关问题仍需板上测试,尤其是 DMA、cache、flash 掉电和时钟。

8. 调试构建

常用配置:

1
2
3
4
5
6
7
8
9
CONFIG_DEBUG=y
CONFIG_DEBUG_INFO=y
CONFIG_ASSERT=y
CONFIG_THREAD_NAME=y
CONFIG_THREAD_STACK_INFO=y
CONFIG_INIT_STACKS=y
CONFIG_STACK_SENTINEL=y
CONFIG_THREAD_ANALYZER=y
CONFIG_LOG=y

调试功能会改变 timing、stack 和 image size。最终结论需在接近 release 的配置下复测。

9. GDB

1
2
west debug -d build/<name>
west attach -d build/<name>

关键检查:

1
2
3
4
5
6
7
info threads
thread apply all bt
p _current
p _kernel
p/x <device>
info registers
disassemble

优化构建中变量可能被优化掉,必要时仅对问题文件降低优化,而不是长期全局 -O0

10. 启动问题

无输出时按顺序:

  1. reset vector 是否到达;
  2. flash/link address 是否正确;
  3. clock 是否工作;
  4. z_cstart() 是否执行;
  5. console device 是否 status okay;
  6. UART pinctrl/baud;
  7. console driver Kconfig;
  8. init function 返回值;
  9. logging backend 是否启动。

早期 PRE_KERNEL 问题优先用 GPIO、debugger 或 printk,logging 可能尚不可用。

11. Device init 问题

检查构建产物:

1
2
3
rg '<node-name>|<compatible>' build/zephyr/zephyr.dts
rg 'CONFIG_<DRIVER>' build/zephyr/.config
rg '__device_dts_ord' build/zephyr/include/generated/zephyr/devicetree_generated.h

运行时:

  • device_is_ready()
  • init result;
  • shell device list;
  • dependency handles;
  • clock/pinctrl/controller 状态。

12. HardFault/Fatal

记录:

  • reason;
  • exception stack frame;
  • PC/LR/SP;
  • current thread name;
  • stack bounds;
  • fault status registers;
  • build ID/firmware version。

地址解析:

1
2
addr2line -e build/zephyr/zephyr.elf -f -C <pc>
objdump -dS build/zephyr/zephyr.elf

必须使用与设备完全一致、未 strip 的 ELF。

13. Stack 分析

方法:

  • thread analyzer;
  • k_thread_stack_space_get()
  • stack sentinel/canary;
  • compiler stack usage 文件;
  • map 和静态 call graph;
  • 压力场景下 high-water mark。

分别评估:

  • main stack;
  • ISR stack;
  • system workqueue;
  • logging;
  • shell;
  • network RX/TX;
  • 每个业务线程。

14. 性能与实时性

测量对象:

  • ISR latency;
  • ISR-to-thread latency;
  • context switch;
  • mutex contention;
  • workqueue delay;
  • timeout jitter;
  • flash operation blocking;
  • network tail latency。

使用 cycle counter、timing API、tracing/SystemView。避免在被测路径中大量 logging。

15. 内存分析

1
2
west build -d build/<name> -t rom_report
west build -d build/<name> -t ram_report

重点:

  • .text/.rodata
  • .data/.bss/noinit
  • iterable metadata;
  • thread stacks;
  • heap;
  • network buffers;
  • logging buffer;
  • filesystem cache。

功能关闭后若体积未下降,检查引用、select、always-linked library 和 LTO。

16. 配置归档

发布应保存:

  • source commit;
  • west manifest revisions;
  • toolchain/SDK;
  • board/revision;
  • build command;
  • .config
  • zephyr.dts
  • map/ELF;
  • signing metadata;
  • generated version/build descriptor。

只保存 prj.conf 无法重现完整构建。

17. 安全检查

  • userspace/MPU 是否按威胁模型启用;
  • stack protection;
  • fortify;
  • assert 在 release 的策略;
  • entropy/CSPRNG;
  • management shell/MCUmgr 暴露;
  • debug port;
  • firmware signing/rollback;
  • persistent data 权限;
  • network credentials;
  • fatal/coredump 是否泄露敏感信息。

18. 常见误区

  • 把 board defconfig 当应用配置;
  • 手改生成文件;
  • 只看 prj.conf 不看 .config
  • DEVICE_DT_GET() 后不检查 ready;
  • 在 ISR/workqueue 中做长阻塞;
  • 用 heap 解决所有实时对象;
  • 认为 logging 不影响时序;
  • 忽略 MCUboot header/trailer 对 flash 容量的影响;
  • 只测试正常升级,不做掉电和回滚。

Zephyr 本工程板级定制分析

Zephyr 本工程板级定制分析

1. 定制范围

当前 Zephyr 主树中可明确识别的项目板级目录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
boards/by/
├─ ipmc/
│ ├─ board.yml
│ ├─ ipmc.dts
│ ├─ ipmc_defconfig
│ ├─ ipmc_f103.dts
│ ├─ ipmc_f103_defconfig
│ ├─ Kconfig.ipmc
│ └─ Kconfig.ipmc_f103
├─ sct6030_lcd/
│ ├─ board.yml
│ ├─ sct6030_lcd.dts
│ ├─ sct6030_lcd_defconfig
│ └─ Kconfig.sct6030_lcd
└─ sct6030_smb/
├─ board.yml
├─ sct6030_smb.dts
├─ sct6030_smb_defconfig
└─ Kconfig.sct6030_smb

此外存在项目 binding:

1
dts/bindings/ipmi/ipmi,gpio-sensor.yaml

对应应用驱动实现位于:

1
app/ipmc/sensor/gpio.c

说明项目采用“板级硬件描述放 Zephyr 主树,业务驱动放 application project”的混合组织方式。

2. board.yml

ipmc/board.yml 定义两个 board:

1
2
ipmc      → GD32F470
ipmc_f103 → STM32F103XE

sct6030_lcdsct6030_smb

1
2
vendor: olimex
SoC: stm32f103xb

这里 vendor 使用参考板厂商 olimex,而目录位于自定义 boards/by。技术上可以工作,但从维护和 board catalog 语义上看,最好为产品定义自己的 vendor prefix,避免被误认为 Olimex 官方板。

3. SCT6030 LCD

sct6030_lcd.dts 复用:

1
boards/olimex/stm32_h103/olimex_stm32_h103.dts

主要修改:

  • model:SCT6030 LCD board
  • console/shell:USART1;
  • HSE:8 MHz;
  • PLL:mul = 27
  • USART1 enabled。

defconfig 启用:

  • serial/console/UART console;
  • GPIO;
  • clock control;
  • pinctrl。

注意点

  1. include 使用相对路径 ../../olimex/...,对 boards 目录布局敏感;优先使用稳定 include path。
  2. compatible 仍为 olimex,stm32-h103,无法从 compatible 区分产品板。
  3. PLL 倍频必须核对 STM32F1 时钟驱动的 xtpre 语义和最终 SYSCLK,避免超频。
  4. DTS 未在本文件中定义 MCUboot partitions,若应用启用 MCUboot,应确认继承的参考板 DTS 或 overlay 提供正确布局。

4. SCT6030 SMB

sct6030_smb.dts 同样复用 STM32 H103,差异:

  • console/shell:USART2;
  • HSE disabled;
  • HSI:8 MHz enabled;
  • PLL 输入 HSI,mul = 27
  • USART2 enabled;
  • independent watchdog enabled。

注意点

  • 内部 HSI 精度低于外部晶振,UART、时间基准和温漂需求需实测;
  • watchdog 设备 enabled 不代表应用已 setup/feed;
  • LCD 与 SMB 的 clock source 不同,共享固件配置时不能假定时钟完全一致。

5. IPMC(GD32F470)

ipmc.dts 基于:

1
gd/gd32f4xx/gd32f470vg.dtsi

chosen:

  • console/shell:USART0;
  • MCUmgr UART:USART5;
  • code partition:slot0_partition
  • settings partition:settings_partition
  • entropy:TRNG。

aliases:

  • mcuboot-led0
  • watchdog0

但当前 board DTS 中大量硬件被显式 disabled:

  • I2C0/1/2;
  • LED;
  • timer0;
  • GPIO A–E;
  • MPU/SYSCFG/EXTI;
  • USART2;
  • SPI/flash;
  • DMA;
  • watchdog;
  • TRNG。

USART5 和 internal flash 保持 enabled。

配置矛盾风险

chosen/aliases 只是引用节点,不会自动把节点改为 okay:

  • zephyr,entropy = &trng,但 &trng status = "disabled"
  • watchdog0 = &fwdgt,但 watchdog disabled;
  • mcuboot-led0 = &led1,但 LED disabled;
  • hand_switch disabled,且其 GPIO controller 也 disabled。

这可能是“board 提供默认关闭,application overlay 再开启”的设计。每个实际应用构建都必须检查最终 zephyr.dts,不能仅看 board DTS。

6. IPMC F103

ipmc_f103.dts 复用 stm32f103_mini.dts,并覆盖:

  • SRAM:64 KiB;
  • flash:512 KiB;
  • console/shell:USART1;
  • HSE:8 MHz;
  • PLL:mul = 27
  • GPIOA enabled,其余多组 GPIO disabled;
  • USART1 enabled;
  • UART4/UART5/I2C2 定义 pinctrl 但 disabled。

Flash layout

1
2
3
0x08000000 + 0x00000 : MCUboot  16 KiB
0x08000000 + 0x04000 : slot 0 248 KiB
0x08000000 + 0x42000 : slot 1 248 KiB

合计:

1
16 + 248 + 248 = 512 KiB

两个 slot 等大,适合常规 swap/overwrite;没有 scratch partition,因此 MCUboot 应使用 move、overwrite、direct-XIP 等无需 scratch 的模式。

关键风险

  • 16 KiB MCUboot 对启用签名、日志、串口恢复的配置可能不足,应以 boot ELF/map 验证;
  • 248 KiB slot 还需扣除 header、TLV 和 trailer;
  • STM32F1 flash page 大小、write block size 与 move swap sector 约束需核对;
  • DTS 中 write-block-size = <4> 必须与 flash driver 实际写粒度一致。

7. GPIO Sensor Binding

ipmi,gpio-sensor.yaml

  • include ipmi,sensor.yaml
  • gpios:phandle-array;
  • invert:boolean;
  • debounce:int,默认 0。

当前 gpiosrequired: true 被注释,但驱动无条件使用:

1
GPIO_DT_SPEC_INST_GET(n, gpios)

因此 enabled 节点若缺少 gpios 会在编译期失败。binding 应将 gpios 明确设为 required,错误信息会更直接。

8. GPIO Sensor 实例化

app/ipmc/sensor/gpio.c

1
2
3
4
5
DT_DRV_COMPAT ipmi_gpio_sensor
→ DT_INST_FOREACH_STATUS_OKAY(SENS_DT_INIT)
→ 为每个 enabled instance 创建 gpio_sens_t
→ 读取 debounce/invert/gpios
→ SENS_INIT(...)

ipmc.dtshand_switch 默认 disabled;应用 overlay 必须改为 okay 才会生成实例。

9. West Manifest 定制

zephyr/west.yml 除标准模块外还声明:

1
2
app_common → app/common
ipmc → app/ipmc

说明应用目录也按 west project 参与 workspace。发布时应固定:

  • project remote;
  • revision;
  • path;
  • manifest repository commit。

当前片段未给这两个 project 显式 revision/remote,它们会继承 manifest defaults。若依赖纯本地目录,需确认全新 workspace 执行 west update 时仍可复现。

10. HC32/XHSC 移植

boards/by 外,本树还包含一套较完整的厂商移植:

1
2
3
4
5
boards/hc/hc32f4a0/
soc/xhsc/hc32f4xx/
dts/arm/xhsc/
modules/hal_xhsc/
../modules/hal/xhsc/

对应驱动覆盖 clock control、UART、flash、I2C、DMA、pinctrl、counter 和 interrupt controller 等类别。这部分不是普通 board overlay,而是从 SoC、DTS、HAL glue 到 driver 的完整移植链。

维护时应联动检查:

  • hal_xhsc west project revision;
  • SoC Kconfig 与 linker memory;
  • DTS interrupt/clock/pinctrl cells;
  • driver init priority;
  • board hc32f4a0_defconfig
  • SDK 0.16.9 工具链兼容性。

当前 west manifest 中 hal_xhsc 未显式固定 revision,干净环境复现是主要风险。

11. BOARD=bmc 不一致

app/common/common.makefile 默认:

1
BOARD ?= bmc

但当前整个 zephyr_rtos 中未找到名称为 bmcboard.ymlbmc.dts。这意味着:

  • 默认值可能是旧板名;
  • 实际构建必须从命令行传入 board;
  • 或 board 来自未包含的外部 module。

若无外部 board root,直接使用默认值会在 CMake board 查找阶段失败。环境文档中的构建命令若未传 BOARD,应结合实际构建环境修正。

同时,当前 .west/config 的默认 board 是 ipmc。但 common.makefile 在命令中显式传入 -b $(BOARD),所以 Makefile 的 bmc 会覆盖 west 默认值,不能依赖 .west/config 自动纠正。

12. 板级维护建议

  1. 为产品板建立正式 vendor prefix;
  2. 避免相对跨目录 include;
  3. chosen/alias 所指节点默认保持 okay,或在文档中明确 overlay 责任;
  4. binding 的驱动必需属性设为 required;
  5. 每个 board 增加 hello_world、GPIO、console 编译/运行测试;
  6. MCUboot board 增加 partition 容量和 trailer 自动检查;
  7. 固化 west project revision;
  8. 清理失效的默认 BOARD=bmc
  9. 对 PLL、flash/SRAM 容量和 pinctrl 与原理图交叉审核;
  10. 发布保存最终 .configzephyr.dts

13. 构建核对命令

1
2
3
4
5
west boards | rg 'ipmc|sct6030'

west build -b ipmc_f103 -d build/ipmc_f103 samples/hello_world
west build -b sct6030_lcd -d build/sct6030_lcd samples/hello_world
west build -b sct6030_smb -d build/sct6030_smb samples/hello_world

查看最终硬件状态:

1
2
rg 'chosen|slot0_partition|slot1_partition|trng|fwdgt|usart' \
build/<board>/zephyr/zephyr.dts

Ceph OSD 启动时做了什么

这个问题非常好 👍,已经从“组件理解”进入到“系统行为 + 性能机制”层面了。
我给你分两大块讲清楚:


一、Ceph OSD 启动时做了什么?

可以把 OSD 启动理解成 5 个阶段:


🧱 1️⃣ 设备与存储引擎初始化(BlueStore)

OSD 启动第一件事:

1
打开磁盘 → 初始化 BlueStore → 加载元数据

核心动作:

  • 打开 block 设备(NVMe / SSD / SPDK bdev)
  • 初始化 BlueStore
  • 挂载 RocksDB
  • 读取 superblock(OSD 信息)

👉 如果是第一次启动:

  • 格式化 BlueStore
  • 初始化 allocator / WAL / DB


🌐 2️⃣ 网络与集群注册

OSD 会连接 Ceph 集群:

1
OSD → Monitor(MON)

作用:

  • 获取 cluster map
  • 获取 OSD map
  • 注册自己(上线)

👉 包括:

  • public network(客户端流量)
  • cluster network(OSD 间复制)


🧠 3️⃣ 加载 PG(Placement Group)

这是核心步骤:

1
OSD 加载属于自己的 PG

每个 PG:

  • 对应一部分数据
  • 有自己的状态机

👉 加载内容:

  • PG metadata
  • 对象信息
  • 日志(log)


🔁 4️⃣ Recovery / Peering

如果集群不是干净状态:

1
OSD 会进入恢复流程

包括:

  • peering(主从同步)
  • recovery(数据补齐)
  • backfill(大规模重建)


⚙️ 5️⃣ 启动 IO 处理线程

OSD 最终进入:

1
等待客户端 IO

启动:

  • op queue(请求队列)
  • worker 线程
  • messenger 网络线程


二、OSD 如何支持“大流量并发”?

这是重点,我给你拆成 6 大机制。


🚀 1️⃣ PG 分片(核心并发单位)

👉 Ceph 最大的并发设计:

1
数据 → 切分为 PG(Placement Group)

例如:

1
1000个PG → 分布在多个OSD

👉 每个 PG:

  • 独立处理 IO
  • 独立锁(避免全局锁)

✔ 好处:

1
天然水平扩展


🚀 2️⃣ 多队列 + 多线程模型

OSD 内部:

1
client → op queue → worker threads

👉 有:

  • shard queue(分片队列)
  • 多 worker 并行处理

类似:

1
2
CPU0 → PG1, PG2
CPU1 → PG3, PG4


🚀 3️⃣ 异步 IO(AIO)

BlueStore 使用:

1
aio / io_uring / SPDK(可选)

👉 特点:

  • 非阻塞
  • pipeline 化

1
提交 IO → 不等待 → 继续处理


🚀 4️⃣ Transaction 批处理

ObjectStore 层:

1
多个操作 → 合并成一个 Transaction

👉 优点:

  • 减少 IO 次数
  • 减少锁竞争
  • 提高吞吐


🚀 5️⃣ RocksDB 优化(元数据并发)

RocksDB 提供:

  • LSM-tree 写优化
  • memtable + compaction
  • batch 写

👉 避免:

1
频繁随机写


🚀 6️⃣ 网络层优化(Messenger)

Ceph 使用:

1
Async Messenger(msgr v2)

特点:

  • 多连接
  • 零拷贝
  • RDMA(可选)


三、如果结合 SPDK(更强)

当你引入:

SPDK


IO路径变成:

1
2
3
4
5
6
7
OSD

BlueStore

SPDK bdev

NVMe

提升:

  • 无 syscall
  • 无中断
  • 多队列直达硬件


四、整体并发模型(总结图)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
客户端并发请求

Messenger(网络)

PG 分片(并发核心)

Op Queue(多队列)

Worker Threads

Transaction(批处理)

BlueStore

(SPDK / AIO)

NVMe SSD

五、关键设计总结(非常重要)


❗1. 并发单位不是线程

👉 是:

1
PG(Placement Group)

❗2. 数据路径是无锁/低锁设计

  • PG 内部锁
  • 避免全局锁

❗3. IO 全部异步化

  • 网络 async
  • 磁盘 async
  • 事务 async

❗4. 扩展靠“横向扩展”

1
加 OSD → 加性能

六、一句话总结

👉 OSD 启动时完成设备初始化、PG加载和集群注册;运行时通过 PG 分片 + 多队列 + 异步 IO + 批处理 来支撑大规模并发


🚀 如果你想再深入(建议)

可以继续往这几个“真正核心”挖:


🔥 1. PG 状态机(peering / active)

🔥 2. OSD op queue 调度算法(wpq / mclock)

🔥 3. BlueStore 并发模型(txc / kv_sync_thread)

🔥 4. SPDK 在 Ceph 中的线程绑定方式


你可以直接说:

👉 “讲 OSD op queue 调度”
👉 或 “讲 PG 状态机”

我可以带你进入源码级理解 👍

OpenStack 和 Kubernetes 详细介绍

OpenStack 和 Kubernetes 详细介绍

目录

  1. OpenStack 详细介绍
  2. Kubernetes 详细介绍
  3. OpenStack 与 Kubernetes 的关系
  4. 使用场景对比
  5. 集成方案

OpenStack 详细介绍

1. OpenStack 概述

OpenStack 是一个开源的云计算平台,用于构建和管理私有云和公有云基础设施。它提供了一套完整的IaaS(基础设施即服务)解决方案。

1.1 核心特点

  • 开源:Apache 2.0 许可证
  • 模块化:由多个独立但协作的服务组成
  • 可扩展:支持大规模部署
  • API驱动:提供RESTful API
  • 多租户:支持多租户隔离
  • 厂商中立:不绑定特定硬件或软件厂商

1.2 发展历史

  • 2010年:由Rackspace和NASA共同发起
  • 2012年:发布第一个稳定版本(Essex)
  • 至今:每6个月发布一个新版本,采用字母顺序命名(如Zed、Antelope、Bobcat等)

2. OpenStack 架构

2.1 整体架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌─────────────────────────────────────────────────────────┐
│ Horizon (Dashboard) │
│ Web UI 管理界面 │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ Keystone (Identity) │
│ 认证、授权、服务目录 │
└─────────────────────────────────────────────────────────┘

┌───────────────────┼───────────────────┐
│ │ │
┌───────▼──────┐ ┌─────────▼────────┐ ┌──────▼──────┐
│ Nova │ │ Neutron │ │ Cinder │
│ (Compute) │ │ (Networking) │ │ (Block) │
│ 计算服务 │ │ 网络服务 │ │ 块存储 │
└──────────────┘ └──────────────────┘ └─────────────┘
│ │ │
┌───────▼──────┐ ┌─────────▼────────┐ ┌──────▼──────┐
│ Glance │ │ Swift │ │ Heat │
│ (Image) │ │ (Object Store) │ │ (Orchestration)│
│ 镜像服务 │ │ 对象存储 │ │ 编排服务 │
└──────────────┘ └──────────────────┘ └─────────────┘

2.2 核心组件

2.2.1 Keystone - 身份认证服务

功能

  • 认证(Authentication):验证用户身份
  • 授权(Authorization):管理用户权限
  • 服务目录(Service Catalog):维护所有服务的端点信息
  • 令牌(Token):生成和管理访问令牌

关键概念

  • User:用户
  • Project/Tenant:项目/租户
  • Role:角色
  • Service:服务
  • Endpoint:服务端点
  • Token:访问令牌

API端点

1
2
3
POST /v3/auth/tokens  # 获取令牌
GET /v3/projects # 获取项目列表
GET /v3/users # 获取用户列表
2.2.2 Nova - 计算服务

功能

  • 虚拟机管理:创建、删除、启动、停止虚拟机
  • 资源调度:将虚拟机调度到合适的计算节点
  • 生命周期管理:管理虚拟机的完整生命周期
  • 资源监控:监控计算资源使用情况

架构组件

  • Nova-API:接收和处理API请求
  • Nova-Scheduler:调度器,决定虚拟机创建在哪个节点
  • Nova-Compute:计算节点上的服务,管理虚拟机
  • Nova-Conductor:数据库访问代理,提供安全的数据访问
  • Nova-Cert:证书管理(已弃用)
  • Nova-Consoleauth:控制台认证
  • Nova-Novncproxy:VNC代理

调度算法

  • Filter Scheduler:基于过滤器的调度
    • AvailabilityZoneFilter:可用区过滤
    • RamFilter:内存过滤
    • DiskFilter:磁盘过滤
    • ComputeFilter:计算节点过滤
  • Chance Scheduler:随机调度
  • Simple Scheduler:简单调度(已弃用)

支持的Hypervisor

  • KVM:Linux内核虚拟化(最常用)
  • Xen:Xen hypervisor
  • VMware vSphere:VMware虚拟化
  • Hyper-V:Microsoft虚拟化
  • Docker:容器支持(通过nova-docker)
2.2.3 Neutron - 网络服务

功能

  • 网络管理:创建和管理虚拟网络
  • 子网管理:管理IP地址分配
  • 路由器管理:提供虚拟路由器功能
  • 安全组:提供防火墙功能
  • 负载均衡:提供负载均衡服务(LBaaS)
  • VPN服务:提供VPN连接(VPNaaS)

架构组件

  • Neutron-Server:API服务器
  • Neutron-Agent:网络代理
    • L3 Agent:三层路由代理
    • DHCP Agent:DHCP代理
    • OVS Agent:Open vSwitch代理
    • Linux Bridge Agent:Linux Bridge代理
  • Neutron-Plugin:插件
    • ML2 Plugin:模块化二层插件(最常用)
    • OVS Plugin:Open vSwitch插件
    • Linux Bridge Plugin:Linux Bridge插件

网络类型

  • Flat Network:扁平网络,所有虚拟机在同一网络
  • VLAN Network:基于VLAN的隔离网络
  • VXLAN Network:基于VXLAN的覆盖网络
  • GRE Network:基于GRE隧道的网络
  • Geneve Network:基于Geneve的网络

网络模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌─────────────────────────────────────────┐
│ External Network │
│ (Internet/Public Network) │
└─────────────────┬───────────────────────┘

┌─────────▼─────────┐
│ Router (L3) │
│ (Neutron Router) │
└─────────┬─────────┘

┌─────────▼─────────┐
│ Private Network │
│ (Tenant Network)│
└─────────┬─────────┘

┌─────────────┼─────────────┐
│ │ │
┌───▼───┐ ┌────▼────┐ ┌───▼───┐
│ VM1 │ │ VM2 │ │ VM3 │
└───────┘ └─────────┘ └───────┘
2.2.4 Cinder - 块存储服务

功能

  • 卷管理:创建、删除、附加、分离卷
  • 快照管理:创建和管理卷快照
  • 备份管理:卷备份和恢复
  • 卷类型:支持不同类型的存储后端

架构组件

  • Cinder-API:API服务器
  • Cinder-Scheduler:调度器,选择存储后端
  • Cinder-Volume:卷服务,与存储后端交互
  • Cinder-Backup:备份服务

支持的存储后端

  • LVM:本地逻辑卷管理
  • Ceph RBD:Ceph块设备
  • NFS:网络文件系统
  • GlusterFS:Gluster文件系统
  • iSCSI:iSCSI存储
  • FC:光纤通道存储
  • EMC VMAX/VNX:EMC存储阵列
  • NetApp:NetApp存储
  • IBM Storwize:IBM存储

卷操作流程

1
2
3
4
5
6
7
8
9
10
11
12
13
创建卷请求


Cinder-API


Cinder-Scheduler (选择后端)


Cinder-Volume (创建卷)


存储后端 (实际创建)
2.2.5 Glance - 镜像服务

功能

  • 镜像管理:上传、下载、删除镜像
  • 镜像格式支持:支持多种镜像格式
  • 镜像元数据:管理镜像元数据
  • 镜像共享:支持镜像在不同项目间共享

支持的镜像格式

  • Raw:原始磁盘镜像
  • QCOW2:QEMU Copy-On-Write(最常用)
  • VHD:Virtual Hard Disk(Hyper-V)
  • VMDK:VMware Virtual Machine Disk
  • ISO:光盘镜像
  • AKI/ARI/AMI:Amazon镜像格式

镜像存储后端

  • File:本地文件系统
  • Swift:OpenStack对象存储
  • Ceph RBD:Ceph块设备
  • S3:Amazon S3
  • HTTP:HTTP服务器
  • Sheepdog:Sheepdog分布式存储
2.2.6 Swift - 对象存储服务

功能

  • 对象存储:存储非结构化数据
  • RESTful API:提供HTTP API
  • 高可用性:通过复制保证数据可用性
  • 可扩展性:支持大规模扩展

架构组件

  • Proxy Server:代理服务器,处理API请求
  • Object Server:对象服务器,存储实际数据
  • Container Server:容器服务器,管理容器
  • Account Server:账户服务器,管理账户

数据分布

  • Ring:数据分布算法
  • Replication:数据复制(默认3副本)
  • Consistency:一致性检查

使用场景

  • 静态文件存储
  • 备份和归档
  • 媒体文件存储
  • 日志存储
2.2.7 Heat - 编排服务

功能

  • 模板编排:使用模板定义基础设施
  • 资源管理:管理多个资源的生命周期
  • 自动化部署:自动化部署复杂应用
  • 依赖管理:管理资源间的依赖关系

模板格式

  • HOT (Heat Orchestration Template):YAML格式
  • CFN (CloudFormation):AWS CloudFormation格式

示例模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
heat_template_version: 2015-04-30

description: Simple template to deploy a single instance

resources:
my_instance:
type: OS::Nova::Server
properties:
image: cirros
flavor: m1.small
networks:
- network: private

outputs:
instance_ip:
description: IP address of the instance
value: { get_attr: [my_instance, first_address] }
2.2.8 Horizon - 仪表板服务

功能

  • Web UI:提供Web管理界面
  • 资源管理:通过UI管理所有资源
  • 监控:查看资源使用情况
  • 多租户支持:支持多租户管理

主要功能模块

  • 实例管理:创建、管理虚拟机
  • 网络管理:管理网络、子网、路由器
  • 卷管理:管理块存储卷
  • 镜像管理:管理镜像
  • 用户管理:管理用户和项目
  • 监控:查看使用统计

3. OpenStack 部署架构

3.1 典型部署架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
┌─────────────────────────────────────────────────────────┐
│ Controller Node │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Keystone │ │ Nova │ │ Neutron │ │ Cinder │ │
│ │ API │ │ API │ │ Server │ │ API │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Glance │ │ Heat │ │ Horizon │ │ MySQL │ │
│ │ API │ │ Engine │ │ │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ │
│ │ RabbitMQ │ │ Memcached│ │
│ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────┘

┌───────────────────┼───────────────────┐
│ │ │
┌───────▼──────┐ ┌─────────▼────────┐ ┌──────▼──────┐
│ Compute Node │ │ Compute Node │ │ Compute Node│
│ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐│
│ │Nova- │ │ │ │Nova- │ │ │ │Nova- ││
│ │Compute │ │ │ │Compute │ │ │ │Compute ││
│ └──────────┘ │ │ └──────────┘ │ │ └──────────┘│
│ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐│
│ │Neutron │ │ │ │Neutron │ │ │ │Neutron ││
│ │Agent │ │ │ │Agent │ │ │ │Agent ││
│ └──────────┘ │ │ └──────────┘ │ │ └──────────┘│
│ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐│
│ │KVM │ │ │ │KVM │ │ │ │KVM ││
│ └──────────┘ │ │ └──────────┘ │ │ └──────────┘│
└──────────────┘ └─────────────────┘ └─────────────┘

3.2 高可用部署

Controller节点高可用

  • Active-Active:多个Controller节点,负载均衡
  • 数据库高可用:MySQL Galera集群或MariaDB集群
  • 消息队列高可用:RabbitMQ集群
  • API高可用:使用HAProxy负载均衡

计算节点

  • 通常不需要高可用(虚拟机本身提供高可用)
  • 可以通过迁移实现节点维护

4. OpenStack 工作流程

4.1 创建虚拟机流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1. 用户通过Horizon或CLI发送创建VM请求


2. Nova-API接收请求


3. Keystone验证用户身份和权限


4. Nova-Scheduler选择计算节点


5. Nova-Compute在选定节点创建VM

├─► Glance获取镜像
├─► Neutron分配网络
└─► Cinder附加卷(如果需要)


6. VM创建完成,返回信息给用户

4.2 网络创建流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1. 用户创建网络请求


2. Neutron-Server接收请求


3. ML2 Plugin处理网络创建


4. Neutron-Agent配置网络

├─► OVS Agent配置Open vSwitch
├─► DHCP Agent启动DHCP服务
└─► L3 Agent配置路由(如果需要)


5. 网络创建完成

5. OpenStack 优势与挑战

5.1 优势

  • 开源免费:无许可证费用
  • 功能完整:提供完整的IaaS功能
  • 可扩展:支持大规模部署
  • 厂商中立:不绑定特定厂商
  • 社区活跃:大型开源社区支持
  • 标准化:遵循开放标准

5.2 挑战

  • 复杂性:组件多,部署和维护复杂
  • 学习曲线:需要深入理解各个组件
  • 资源消耗:Controller节点资源消耗较大
  • 版本升级:升级过程复杂
  • 性能调优:需要针对场景进行调优

Kubernetes 详细介绍

1. Kubernetes 概述

Kubernetes (K8s) 是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。

1.1 核心特点

  • 容器编排:自动化容器部署和管理
  • 自我修复:自动重启失败的容器
  • 水平扩展:根据负载自动扩展
  • 服务发现:自动服务发现和负载均衡
  • 滚动更新:支持零停机更新
  • 配置管理:集中管理配置和密钥

1.2 发展历史

  • 2014年:Google开源Kubernetes(基于Borg系统)
  • 2015年:CNCF(云原生计算基金会)成立,Kubernetes成为首个项目
  • 2016年:发布1.0版本
  • 至今:每3个月发布一个新版本

2. Kubernetes 架构

2.1 整体架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
┌─────────────────────────────────────────────────────────┐
│ Control Plane │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ API │ │ etcd │ │ Scheduler│ │
│ │ Server │ │ │ │ │ │
│ └────┬─────┘ └──────────┘ └──────────┘ │
│ │ │
│ ┌────▼─────┐ ┌──────────┐ │
│ │ Controller│ │ Cloud │ │
│ │ Manager │ │ Controller│ │
│ └───────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────┘

┌───────────────────┼───────────────────┐
│ │ │
┌───────▼──────┐ ┌─────────▼────────┐ ┌──────▼──────┐
│ Worker Node │ │ Worker Node │ │ Worker Node │
│ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐│
│ │ Kubelet │ │ │ │ Kubelet │ │ │ │ Kubelet ││
│ └──────────┘ │ │ └──────────┘ │ │ └──────────┘│
│ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐│
│ │ Kube- │ │ │ │ Kube- │ │ │ │ Kube- ││
│ │ proxy │ │ │ │ proxy │ │ │ │ proxy ││
│ └──────────┘ │ │ └──────────┘ │ │ └──────────┘│
│ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐│
│ │ Container│ │ │ │ Container│ │ │ │ Container││
│ │ Runtime │ │ │ │ Runtime │ │ │ │ Runtime ││
│ └──────────┘ │ │ └──────────┘ │ │ └──────────┘│
└──────────────┘ └─────────────────┘ └─────────────┘

2.2 核心组件

2.2.1 API Server (kube-apiserver)

功能

  • API入口:所有操作的统一入口
  • 认证授权:处理认证和授权
  • 数据验证:验证API请求
  • etcd交互:与etcd交互存储数据

特点

  • RESTful API
  • 支持多种认证方式(证书、Token、Basic Auth等)
  • 支持RBAC(基于角色的访问控制)
  • 支持审计日志

API版本

  • v1:稳定版本
  • v1beta1:测试版本
  • v1alpha1:实验版本
2.2.2 etcd

功能

  • 分布式键值存储:存储集群状态
  • 配置存储:存储集群配置
  • 服务发现:支持服务发现
  • 一致性保证:使用Raft算法保证一致性

存储内容

  • Pod信息
  • Service信息
  • ConfigMap和Secret
  • 集群状态
  • 节点信息

高可用

  • 通常部署3个或5个节点
  • 使用Raft算法保证一致性
2.2.3 Scheduler (kube-scheduler)

功能

  • Pod调度:决定Pod运行在哪个节点
  • 资源匹配:匹配节点资源
  • 约束检查:检查节点约束(taint、affinity等)

调度流程

1
2
3
4
5
6
7
8
9
10
11
1. 过滤(Filtering)
- 检查节点资源是否足够
- 检查节点是否满足约束
- 过滤掉不满足条件的节点

2. 评分(Scoring)
- 对每个节点打分
- 考虑资源利用率、亲和性等

3. 选择(Selecting)
- 选择得分最高的节点

调度策略

  • LeastRequestedPriority:优先选择资源使用最少的节点
  • BalancedResourceAllocation:平衡资源分配
  • NodeAffinityPriority:节点亲和性优先级
  • InterPodAffinityPriority:Pod亲和性优先级
2.2.4 Controller Manager (kube-controller-manager)

功能

  • 控制器管理:运行各种控制器
  • 状态同步:确保期望状态和实际状态一致
  • 自动修复:自动修复异常状态

内置控制器

  • Replication Controller:管理副本数
  • Deployment Controller:管理Deployment
  • StatefulSet Controller:管理StatefulSet
  • DaemonSet Controller:管理DaemonSet
  • Job Controller:管理Job
  • Node Controller:管理节点
  • Service Controller:管理Service
  • Endpoint Controller:管理Endpoint
  • Namespace Controller:管理Namespace
  • PersistentVolume Controller:管理PV
2.2.5 Cloud Controller Manager

功能

  • 云平台集成:与云平台(AWS、Azure、GCP等)集成
  • 节点管理:管理云平台上的节点
  • 负载均衡:管理云平台负载均衡器
  • 路由:管理云平台路由
2.2.6 Kubelet

功能

  • Pod管理:在节点上管理Pod生命周期
  • 容器运行时交互:与容器运行时(Docker、containerd等)交互
  • 资源监控:监控节点资源使用
  • 健康检查:执行健康检查

工作流程

1
2
3
4
5
1. 从API Server获取Pod清单
2. 创建Pod所需的容器
3. 挂载卷
4. 执行健康检查
5. 报告Pod状态给API Server
2.2.7 Kube-proxy

功能

  • 服务代理:实现Service的负载均衡
  • 网络规则:管理iptables或IPVS规则
  • 服务发现:实现服务发现

代理模式

  • userspace:用户空间代理(已弃用)
  • iptables:iptables代理(默认)
  • ipvs:IPVS代理(高性能)

工作原理

1
2
3
4
5
6
7
8
Service (ClusterIP: 10.96.0.1)


Kube-proxy创建iptables规则

├─► Pod 1 (10.244.1.2)
├─► Pod 2 (10.244.1.3)
└─► Pod 3 (10.244.2.1)
2.2.8 Container Runtime

功能

  • 容器管理:创建、运行、停止容器
  • 镜像管理:拉取和管理镜像
  • 资源隔离:提供资源隔离

支持的运行时

  • Docker:Docker引擎(已弃用,使用containerd)
  • containerd:CNCF容器运行时(推荐)
  • CRI-O:OCI容器运行时
  • rkt:CoreOS容器运行时(已弃用)

3. Kubernetes 核心概念

3.1 Pod

定义:Pod是Kubernetes中最小的部署单元,包含一个或多个容器。

特点

  • 共享网络命名空间
  • 共享存储卷
  • 共享IPC命名空间
  • 生命周期一致

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80
- name: sidecar
image: busybox
command: ['sh', '-c', 'sleep 3600']

3.2 Deployment

定义:Deployment管理Pod的副本和更新。

功能

  • 副本管理:维护指定数量的Pod副本
  • 滚动更新:支持零停机更新
  • 回滚:支持版本回滚
  • 扩缩容:支持水平扩缩容

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80

3.3 Service

定义:Service提供稳定的网络访问端点。

类型

  • ClusterIP:集群内部IP(默认)
  • NodePort:节点端口
  • LoadBalancer:负载均衡器
  • ExternalName:外部名称

示例

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- port: 80
targetPort: 80
type: ClusterIP

3.4 ConfigMap 和 Secret

ConfigMap:存储非敏感配置数据

1
2
3
4
5
6
7
8
apiVersion: v1
kind: ConfigMap
metadata:
name: my-config
data:
config.yaml: |
key: value
port: 8080

Secret:存储敏感数据(密码、密钥等)

1
2
3
4
5
6
7
8
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
data:
username: YWRtaW4= # base64编码
password: cGFzc3dvcmQ=

3.5 Volume

定义:Volume提供Pod中容器的持久化存储。

类型

  • emptyDir:临时存储
  • hostPath:主机路径
  • persistentVolumeClaim:持久卷声明
  • configMap:ConfigMap卷
  • secret:Secret卷

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: app
image: nginx
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: my-pvc

3.6 Namespace

定义:Namespace提供逻辑隔离,将资源分组。

默认Namespace

  • default:默认命名空间
  • kube-system:系统组件
  • kube-public:公共资源
  • kube-node-lease:节点租约

示例

1
2
3
4
apiVersion: v1
kind: Namespace
metadata:
name: production

4. Kubernetes 工作流程

4.1 Pod 创建流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1. 用户提交Pod定义(kubectl apply)


2. API Server接收请求并验证


3. API Server将Pod信息写入etcd


4. Scheduler监听etcd,发现新Pod


5. Scheduler选择节点并更新Pod信息


6. Kubelet监听etcd,发现分配给自己的Pod


7. Kubelet创建Pod并启动容器

├─► 拉取镜像
├─► 创建容器
├─► 挂载卷
└─► 执行健康检查


8. Kubelet报告Pod状态给API Server

4.2 Service 工作流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1. 用户创建Service


2. API Server接收请求


3. Endpoint Controller创建Endpoint


4. Kube-proxy监听Service和Endpoint变化


5. Kube-proxy更新iptables/IPVS规则


6. 流量通过规则转发到Pod

5. Kubernetes 网络模型

5.1 网络模型

Pod网络

  • 每个Pod有独立的IP地址
  • Pod之间可以直接通信
  • 使用CNI(Container Network Interface)插件

Service网络

  • ClusterIP:虚拟IP,集群内部访问
  • 通过iptables/IPVS实现负载均衡

网络插件

  • Flannel:简单的覆盖网络
  • Calico:基于BGP的网络
  • Weave:覆盖网络
  • Cilium:基于eBPF的网络
  • CNI-Genie:多网络插件支持

5.2 网络架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌─────────────────────────────────────────┐
│ Cluster Network │
│ (Pod CIDR: 10.244.0.0/16) │
└─────────────────┬───────────────────────┘

┌─────────────┼─────────────┐
│ │ │
┌───▼───┐ ┌────▼────┐ ┌───▼───┐
│ Pod1 │ │ Pod2 │ │ Pod3 │
│10.244.│ │10.244. │ │10.244.│
│ 1.2 │ │ 1.3 │ │ 2.1 │
└───────┘ └─────────┘ └───────┘
│ │ │
└─────────────┼─────────────┘

┌─────────▼─────────┐
│ Service │
│ (10.96.0.1) │
└───────────────────┘

6. Kubernetes 存储

6.1 存储架构

PV (PersistentVolume):集群级别的存储资源

PVC (PersistentVolumeClaim):用户对存储的请求

StorageClass:存储类,定义存储类型

工作流程

1
2
3
4
5
6
7
8
9
10
1. 管理员创建PV(或通过StorageClass动态创建)


2. 用户创建PVC请求存储


3. Kubernetes绑定PVC到PV


4. Pod使用PVC挂载存储

6.2 存储类型

本地存储

  • hostPath:主机路径
  • local:本地卷

网络存储

  • NFS:网络文件系统
  • iSCSI:iSCSI存储
  • FC:光纤通道

云存储

  • AWS EBS:AWS弹性块存储
  • Azure Disk:Azure磁盘
  • GCE PD:Google云持久化磁盘

分布式存储

  • Ceph RBD:Ceph块设备
  • GlusterFS:Gluster文件系统
  • CephFS:Ceph文件系统

7. Kubernetes 优势与挑战

7.1 优势

  • 自动化:自动化部署、扩展、管理
  • 可扩展:支持大规模扩展
  • 自我修复:自动重启失败的容器
  • 服务发现:内置服务发现和负载均衡
  • 滚动更新:支持零停机更新
  • 配置管理:集中管理配置
  • 多云支持:支持多云部署

7.2 挑战

  • 学习曲线:概念多,学习曲线陡峭
  • 复杂性:大规模部署复杂
  • 网络:网络配置可能复杂
  • 存储:存储管理需要额外配置
  • 安全:需要正确配置安全策略
  • 监控:需要额外的监控工具

OpenStack 与 Kubernetes 的关系

1. 互补关系

OpenStack 提供基础设施(IaaS):

  • 虚拟机
  • 网络
  • 存储
  • 计算资源

Kubernetes 提供应用编排(容器编排):

  • 容器管理
  • 应用部署
  • 服务编排
  • 自动化运维

2. 集成方案

2.1 OpenStack 上运行 Kubernetes

架构

1
2
3
4
5
6
7
8
9
OpenStack (IaaS层)
├─► Nova创建VM
├─► Neutron提供网络
└─► Cinder提供存储


Kubernetes运行在VM上
├─► Master节点(Control Plane)
└─► Worker节点

优势

  • 利用OpenStack的IaaS能力
  • Kubernetes专注于应用编排
  • 资源隔离和安全性

项目

  • Magnum:OpenStack容器编排服务
  • Kuryr:OpenStack网络集成

2.2 Kubernetes 管理 OpenStack

架构

1
2
3
4
5
Kubernetes作为控制平面

├─► 管理OpenStack组件
├─► 部署OpenStack服务
└─► 监控OpenStack状态

项目

  • OpenStack Helm:使用Helm在K8s上部署OpenStack
  • Airship:使用K8s管理OpenStack生命周期

3. 混合使用场景

3.1 场景1:传统应用 + 容器应用

1
2
3
4
5
OpenStack
├─► 运行传统虚拟机应用
└─► 提供Kubernetes集群资源

└─► Kubernetes运行容器化应用

3.2 场景2:多租户隔离

1
2
3
4
OpenStack提供多租户隔离
├─► Tenant A: Kubernetes集群A
├─► Tenant B: Kubernetes集群B
└─► Tenant C: 传统VM应用

使用场景对比

1. OpenStack 适用场景

  • 私有云建设:企业自建私有云
  • 传统应用迁移:迁移现有虚拟机应用
  • 多租户IaaS:提供基础设施服务
  • 混合云:与公有云集成
  • 合规要求:需要数据本地化

2. Kubernetes 适用场景

  • 微服务架构:容器化微服务应用
  • CI/CD:持续集成和部署
  • 云原生应用:构建云原生应用
  • DevOps:DevOps实践
  • 大规模应用:需要自动化管理的应用

3. 选择建议

选择OpenStack如果

  • 需要完整的IaaS能力
  • 需要运行传统虚拟机应用
  • 需要多租户隔离
  • 需要与现有基础设施集成

选择Kubernetes如果

  • 主要运行容器化应用
  • 需要自动化应用管理
  • 采用微服务架构
  • 需要快速部署和扩展

两者结合如果

  • 需要同时支持传统应用和容器应用
  • 需要IaaS + 容器编排的完整方案
  • 需要多租户隔离的容器环境

集成方案

1. Magnum - OpenStack容器编排服务

功能

  • 在OpenStack上创建Kubernetes集群
  • 管理集群生命周期
  • 集成OpenStack网络和存储

架构

1
2
3
4
5
6
Magnum API

├─► Heat创建Kubernetes集群
├─► Nova创建VM节点
├─► Neutron配置网络
└─► Cinder提供存储

2. Kuryr - OpenStack网络集成

功能

  • 将Kubernetes网络映射到OpenStack网络
  • 使用Neutron提供网络服务
  • 支持OpenStack负载均衡器

3. Cinder CSI - 存储集成

功能

  • Kubernetes通过CSI使用Cinder卷
  • 动态创建PV
  • 支持快照和备份

总结

OpenStack

  • 定位:IaaS平台,提供基础设施服务
  • 核心:虚拟机、网络、存储管理
  • 适用:私有云、传统应用、多租户场景

Kubernetes

  • 定位:容器编排平台,提供应用管理
  • 核心:容器编排、自动化运维、服务发现
  • 适用:云原生应用、微服务、DevOps

关系

  • 互补:OpenStack提供基础设施,Kubernetes提供应用编排
  • 集成:可以在OpenStack上运行Kubernetes,也可以使用Kubernetes管理OpenStack
  • 选择:根据具体需求选择合适的方案或组合使用

Crimson OSD 代码架构分析:三副本和纠删码实现

Crimson OSD 代码架构分析:三副本和纠删码实现

1. 总体架构

Crimson OSD采用分层架构设计,核心组件包括:

1
2
3
4
5
OSD (osd.h/osd.cc)
└── PG (pg.h/pg.cc) - Placement Group,数据分布和复制的基本单位
└── PGBackend (pg_backend.h) - PG后端抽象基类
├── ReplicatedBackend (replicated_backend.h/cc) - 复制后端(三副本)
└── ECBackend (ec_backend.h/cc) - 纠删码后端

1.1 核心类层次结构

1
2
3
4
5
6
7
8
9
PGBackend (抽象基类)
├── ReplicatedBackend (复制后端)
│ ├── 实现三副本复制
│ ├── 管理副本确认机制
│ └── 处理PG committed-to (PCT) 更新
└── ECBackend (纠删码后端)
├── 实现纠删码编码/解码
├── 管理数据分片和校验分片
└── 处理分片恢复

2. 三副本(Replication)实现

2.1 架构设计

ReplicatedBackend 是复制存储的核心实现类,负责:

  1. 事务复制:将写操作复制到所有副本OSD
  2. 确认机制:等待所有副本确认操作完成
  3. 一致性保证:通过PG committed-to (PCT) 机制保证一致性

2.2 关键数据结构

2.2.1 pending_on_t - 待处理事务信息

1
2
3
4
5
6
7
class pending_on_t {
unsigned pending; // 待确认的副本数量
const eversion_t at_version; // 事务版本
const eversion_t last_complete; // 最后完成版本
crimson::osd::acked_peers_t acked_peers; // 已确认的副本列表
seastar::shared_promise<> all_committed; // 所有副本确认完成的promise
};

作用

  • 跟踪一个待处理事务的状态
  • 记录哪些副本已确认
  • 提供所有副本确认完成的future

2.2.2 pending_transactions_t - 待处理事务映射

1
2
using pending_transactions_t = std::map<ceph_tid_t, pending_on_t>;
pending_transactions_t pending_trans; // key为事务ID

作用

  • 维护所有待处理事务的映射
  • 通过事务ID快速查找事务状态

2.3 核心流程

2.3.1 提交事务流程(submit_transaction)

1
2
3
4
5
6
7
8
rep_op_fut_t submit_transaction(
const std::set<pg_shard_t> &pg_shards, // 所有副本分片
const hobject_t& hoid, // 对象句柄
crimson::osd::ObjectContextRef&& new_clone,
ceph::os::Transaction&& txn, // 事务
osd_op_params_t&& osd_op_p,
epoch_t min_epoch, epoch_t max_epoch,
std::vector<pg_log_entry_t>&& log_entries)

执行步骤

  1. 编码事务数据

    1
    2
    3
    bufferlist encoded_txn_p_bl, encoded_txn_d_bl;
    // 编码事务payload和数据部分
    txn.encode(encoded_txn_p_bl, encoded_txn_d_bl, pg.min_peer_features());
  2. 处理日志条目

    1
    2
    3
    4
    5
    6
    7
    8
    9
    bool is_delete = false;
    for (auto &le : log_entries) {
    le.mark_unrollbackable(); // 标记为不可回滚
    if (le.is_delete()) {
    is_delete = true;
    }
    }
    // 更新快照映射
    co_await pg.update_snap_map(log_entries, txn);
  3. 创建待处理事务记录

    1
    2
    3
    4
    5
    6
    7
    const ceph_tid_t tid = shard_services.get_tid();
    auto pending_txn = pending_trans.try_emplace(
    tid,
    pg_shards.size(), // 副本数量(如3)
    osd_op_p.at_version, // 版本号
    pg.get_last_complete() // 最后完成版本
    ).first;
  4. 向所有副本发送复制操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    std::vector<pg_shard_t> to_push_clone;   // 需要推送的clone
    std::vector<pg_shard_t> to_push_delete; // 需要推送的删除操作

    for (const auto& pg_shard : pg_shards) {
    if (pg_shard == whoami) {
    continue; // 跳过自己
    }

    MURef<MOSDRepOp> m;
    if (pg.should_send_op(pg_shard, hoid)) {
    // 副本拥有对象,发送完整的操作
    m = new_repop_msg(pg_shard, hoid, encoded_txn_p_bl,
    encoded_txn_d_bl, osd_op_p,
    min_epoch, map_epoch, log_entries, true, tid);
    } else {
    // 副本不拥有对象,只发送日志条目(不发送实际操作)
    m = new_repop_msg(pg_shard, hoid, encoded_txn_p_bl,
    encoded_txn_d_bl, osd_op_p,
    min_epoch, map_epoch, log_entries, false, tid);
    // 如果对象在副本上缺失,需要后续推送
    if (pg.is_missing_on_peer(pg_shard, hoid)) {
    if (_new_clone) {
    to_push_clone.push_back(pg_shard);
    }
    if (is_delete) {
    to_push_delete.push_back(pg_shard);
    }
    }
    }
    // 记录待确认的副本
    pending_txn->second.acked_peers.push_back({pg_shard, eversion_t{}});
    // 发送消息到副本OSD
    sends->emplace_back(
    shard_services.send_to_osd(pg_shard.osd, std::move(m), map_epoch));
    }
  5. 记录操作到PG日志

    1
    2
    3
    4
    5
    6
    7
    8
    pg.log_operation(
    std::move(log_entries),
    osd_op_p.pg_trim_to,
    osd_op_p.at_version,
    osd_op_p.pg_committed_to,
    true,
    txn,
    false);
  6. 在本地执行事务并等待确认

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    auto all_completed = interruptor::make_interruptible(
    shard_services.get_store().do_transaction(coll, std::move(txn))
    ).then_interruptible([pending_txn, this] {
    // 如果副本数量为1(只有自己),直接完成
    if (--pending_txn->second.pending == 0) {
    pg.complete_write(pending_txn->second.at_version,
    pending_txn->second.last_complete);
    pending_txn->second.all_committed.set_value();
    return seastar::now();
    }
    // 等待所有副本ACK(在got_rep_op_reply中处理)
    return pending_txn->second.all_committed.get_shared_future();
    }).then_interruptible([pending_txn, this, _new_clone, &hoid,
    to_push_delete, to_push_clone] {
    // 所有副本确认后,处理需要推送的对象
    auto acked_peers = std::move(pending_txn->second.acked_peers);
    pending_trans.erase(pending_txn);
    // 如果有新clone需要推送,加入backfill队列
    if (_new_clone && !to_push_clone.empty()) {
    pg.enqueue_push_for_backfill(_new_clone->obs.oi.soid,
    _new_clone->obs.oi.version,
    to_push_clone);
    }
    // 如果有删除操作需要推送,加入backfill队列
    if (!to_push_delete.empty()) {
    pg.enqueue_delete_for_backfill(hoid, {}, to_push_delete);
    }
    // 可能触发PCT更新
    maybe_kick_pct_update();
    return seastar::now();
    });
  7. 返回future

    1
    2
    3
    4
    5
    // 返回发送future和完成future
    co_return std::make_tuple(
    std::move(sends_complete), // 所有发送操作完成的future
    std::move(all_completed) // 所有副本确认完成的future
    );

2.3.2 副本确认流程(got_rep_op_reply)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void ReplicatedBackend::got_rep_op_reply(const MOSDRepOpReply& reply) {
auto it = pending_trans.find(reply.tid);
if (it != pending_trans.end()) {
auto& pending = it->second;
// 查找并更新对应副本的确认状态
for (auto& peer : pending.acked_peers) {
if (peer.shard == reply.from) {
peer.last_complete = reply.last_complete;
break;
}
}
// 减少待确认数量
pending.pending--;

// 如果所有副本都已确认
if (pending.pending == 0) {
// 更新PG的最后完成版本
pg.complete_write(pending.at_version, pending.last_complete);
// 设置promise,使等待的future ready
pending.all_committed.set_value();
// 清理待处理记录
pending_trans.erase(it);
// 可能触发PCT更新
maybe_kick_pct_update();
}
}
}

流程说明

  • 收到副本回复时,根据事务ID查找对应的待处理事务
  • 更新对应副本的确认状态和最后完成版本
  • 减少待确认副本数量
  • 当所有副本都确认后:
    • 更新PG的最后完成版本
    • 设置promise,使等待的future ready
    • 清理已完成的待处理事务记录
    • 可能触发PCT更新

2.3.3 PG Committed-To (PCT) 更新机制

目的:在IO暂停期间,更新PG的committed-to版本,保证一致性。

1
2
3
4
5
6
7
8
9
10
interruptible_future<> send_pct_update() {
// 向acting set中的所有其他OSD发送PCT更新
for (const auto& peer : acting_set) {
if (peer != whoami) {
auto msg = crimson::make_message<MOSDPGPCT>(
pgid, pg.get_pg_committed_to());
shard_services.send_to_osd(peer.osd, msg);
}
}
}

触发时机

  • 当repop队列为空时,启动PCT定时器
  • 定时器触发后发送PCT更新消息

2.4 智能发送机制

2.4.1 条件发送

根据副本是否拥有对象,采用不同的发送策略:

  1. 副本拥有对象 (pg.should_send_op(pg_shard, hoid) == true):

    • 发送完整的操作(send_op = true
    • 包含事务数据和日志条目
    • 副本直接执行操作
  2. 副本不拥有对象 (pg.should_send_op(pg_shard, hoid) == false):

    • 只发送日志条目(send_op = false
    • 不发送实际操作数据
    • 如果对象缺失,标记为需要后续推送(backfill)

优势

  • 减少网络传输:不拥有对象的副本不需要接收完整数据
  • 支持延迟推送:缺失的对象可以通过backfill机制后续推送

2.4.2 Backfill推送

对于缺失的对象,会加入backfill队列:

1
2
3
4
5
6
7
8
9
10
11
12
// Clone对象推送
if (_new_clone && !to_push_clone.empty()) {
pg.enqueue_push_for_backfill(
_new_clone->obs.oi.soid,
_new_clone->obs.oi.version,
to_push_clone);
}

// 删除操作推送
if (!to_push_delete.empty()) {
pg.enqueue_delete_for_backfill(hoid, {}, to_push_delete);
}

2.5 消息类型

2.5.1 MOSDRepOp - 复制操作消息

内容

  • 事务数据(编码后的Transaction)
    • txn_payload:事务payload(Tentacle格式)
    • data:事务数据(或pre-tentacle格式的完整事务)
  • PG日志条目(logbl
  • 对象句柄(hoid
  • 版本信息
    • at_version:事务版本
    • pg_trim_to:PG trim版本
    • pg_committed_to:PG committed-to版本
  • PG统计信息(pg_stats
  • Epoch信息
    • map_epoch:OSDMap epoch
    • min_epoch:最小epoch

标志

  • CEPH_OSD_FLAG_ACK:需要ACK确认
  • CEPH_OSD_FLAG_ONDISK:需要磁盘确认

消息格式

  • Tentacle格式(新格式):txn_payloaddata 分离
  • Pre-tentacle格式(旧格式):所有内容都在 data

2.5.2 MOSDRepOpReply - 复制操作回复

内容

  • 事务ID(tid
  • 发送者信息(from
  • 确认信息
    • last_complete:最后完成版本
  • 错误信息(如果有)

2.5.3 MOSDPGPCT - PG Committed-To更新

内容

  • PG ID(pgid
  • Committed-to版本(pg_committed_to

用途

  • 在IO暂停期间更新PG的committed-to版本
  • 保证所有副本知道已提交的版本范围

3. 纠删码(Erasure Code)实现

3.1 架构设计

ECBackend 是纠删码存储的核心实现类,负责:

  1. 数据编码:将数据分成多个数据分片和校验分片
  2. 数据解码:从部分分片恢复原始数据
  3. 分片管理:管理数据分片和校验分片的分布

3.2 关键概念

3.2.1 纠删码参数

  • K(数据分片数):原始数据分成的分片数量
  • M(校验分片数):生成的校验分片数量
  • 条带宽度(stripe_width):每个条带的大小
  • EC Profile:纠删码配置,包含K、M、算法等

3.2.2 数据分布

1
2
3
4
5
6
7
原始数据 (N bytes)

分成K个数据分片 (每个 N/K bytes)

生成M个校验分片 (每个 N/K bytes)

总共 K+M 个分片,分布在不同的OSD上

容错能力:可以容忍最多M个分片丢失,仍能恢复原始数据。

3.3 核心流程

3.3.1 读取流程(_read)

1
2
3
4
5
6
7
8
9
10
11
ll_read_ierrorator::future<ceph::bufferlist>
ECBackend::_read(const hobject_t& hoid,
const uint64_t off,
const uint64_t len,
const uint32_t flags)
{
// 1. 确定需要读取的分片
// 2. 从多个OSD并行读取分片
// 3. 如果某些分片不可用,使用其他分片恢复
// 4. 解码并返回原始数据
}

实现要点

  • 需要读取至少K个分片才能恢复数据
  • 可以并行从多个OSD读取
  • 如果某些分片丢失,使用纠删码算法恢复

3.3.2 写入流程(submit_transaction)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
rep_op_fut_t ECBackend::submit_transaction(
const std::set<pg_shard_t> &pg_shards, // 所有分片OSD
const hobject_t& hoid,
crimson::osd::ObjectContextRef&& new_clone,
ceph::os::Transaction&& txn,
osd_op_params_t&& osd_op_p,
epoch_t min_epoch, epoch_t max_epoch,
std::vector<pg_log_entry_t>&& log_entries)
{
// 1. 编码数据:将数据分成K个数据分片
// 2. 生成校验:计算M个校验分片
// 3. 分发分片:将K+M个分片发送到不同的OSD
// 4. 等待确认:等待所有分片写入完成
}

实现要点

  • 数据需要先编码成K+M个分片
  • 每个分片写入到不同的OSD
  • 需要等待所有分片写入完成

3.4 当前实现状态

根据代码分析,ECBackend目前是占位实现

1
2
3
4
5
6
7
8
9
ECBackend::_read(...) {
// todo
return seastar::make_ready_future<bufferlist>();
}

ECBackend::submit_transaction(...) {
// todo
return make_ready_future<rep_op_ret_t>(seastar::now(), seastar::now());
}

说明

  • ECBackend的框架已搭建,但具体实现尚未完成
  • 纠删码的编码/解码逻辑需要进一步实现
  • 分片管理和恢复机制需要完善

4. 后端选择机制

4.1 工厂方法(create)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static std::unique_ptr<PGBackend> PGBackend::create(
pg_t pgid,
const pg_shard_t pg_shard,
const pg_pool_t& pool, // 存储池信息
crimson::osd::PG &pg,
crimson::os::CollectionRef coll,
crimson::osd::ShardServices& shard_services,
const ec_profile_t& ec_profile, // 纠删码配置
DoutPrefixProvider &dpp)
{
// 根据存储池类型选择后端
if (pool.is_replicated()) {
// 复制存储池 -> ReplicatedBackend
return std::make_unique<ReplicatedBackend>(
pgid, pg_shard, pg, coll, shard_services, dpp);
} else if (pool.is_erasure()) {
// 纠删码存储池 -> ECBackend
return std::make_unique<ECBackend>(
pg_shard.shard, coll, shard_services,
ec_profile, pool.get_stripe_width(), dpp);
}
}

选择逻辑

  • pool.is_replicated()ReplicatedBackend
  • pool.is_erasure()ECBackend

4.2 存储池配置

存储池类型在创建时确定:

  • 复制池pool_type = replicated,指定副本数(如size=3)
  • 纠删码池pool_type = erasure,指定EC profile(K、M、算法等)

5. 关键设计模式

5.1 策略模式(Strategy Pattern)

  • PGBackend 作为抽象策略接口
  • ReplicatedBackendECBackend 作为具体策略实现
  • 运行时根据存储池类型选择策略

5.2 工厂模式(Factory Pattern)

  • PGBackend::create() 作为工厂方法
  • 根据配置创建相应的后端实例

5.3 观察者模式(Observer Pattern)

  • pending_on_t 使用 seastar::shared_promise 实现异步通知
  • 副本确认时通知等待的future

6. 数据一致性保证

6.1 三副本一致性

  1. 写操作一致性

    • 主OSD接收写请求
    • 向所有副本发送复制操作(MOSDRepOp)
    • 在本地执行事务
    • 等待所有副本ACK确认(MOSDRepOpReply)
    • 更新PG的最后完成版本
    • 返回成功给客户端

    一致性保证

    • 所有副本必须确认操作完成
    • 使用事务ID(tid)跟踪每个操作
    • 通过版本号(at_version)保证顺序
  2. 读操作一致性

    • 从主OSD读取(或从最近的副本读取)
    • 保证读取到已提交的数据
    • 使用PG的committed-to版本判断数据可见性
  3. PCT(PG Committed-To)机制

    • 目的:在IO暂停期间,更新PG的committed-to版本
    • 触发:当repop队列为空时,启动PCT定时器
    • 执行:向acting set中的所有其他OSD发送PCT更新
    • 作用:保证所有副本知道已提交的版本范围
    1
    2
    3
    4
    5
    6
    void maybe_kick_pct_update() {
    if (pending_trans.empty() && !pct_timer.armed()) {
    // 队列为空且定时器未启动,启动PCT更新
    pct_timer.arm(std::chrono::milliseconds(pct_interval));
    }
    }
  4. Acting Set变化处理

    • 当PG的acting set发生变化时,取消所有待处理事务
    • 对所有待处理事务设置异常(actingset_changed
    • 清空待处理事务映射
    • 取消PCT更新

6.2 纠删码一致性

  1. 写操作

    • 编码数据成K+M个分片
    • 写入所有分片
    • 等待所有分片确认
  2. 读操作

    • 读取至少K个分片
    • 解码恢复原始数据
    • 如果分片丢失,使用其他分片恢复

7. 性能优化

7.1 三副本优化

  1. 并行发送:向所有副本并行发送复制操作
  2. 异步确认:使用future/promise异步等待确认
  3. 批量处理:可以批量处理多个操作

7.2 纠删码优化(待实现)

  1. 并行读取:从多个OSD并行读取分片
  2. 增量编码:只编码修改的部分
  3. 缓存分片:缓存常用的分片

8. 总结

8.1 三副本实现

  • 已完成:ReplicatedBackend实现完整
  • 核心功能:事务复制、确认机制、PCT更新
  • 消息机制:MOSDRepOp、MOSDRepOpReply、MOSDPGPCT

8.2 纠删码实现

  • ⚠️ 框架已搭建:ECBackend类结构完整
  • 实现待完成:编码/解码逻辑需要实现
  • 分片管理:分片分布和恢复机制需要完善

8.3 架构优势

  1. 清晰的抽象:PGBackend提供统一接口
  2. 灵活扩展:易于添加新的存储后端类型
  3. 异步设计:充分利用Seastar的异步能力
  4. 类型安全:使用C++类型系统保证正确性

9. 相关文件

9.1 核心文件

  • pg_backend.h/cc - PG后端抽象基类
  • replicated_backend.h/cc - 复制后端实现
  • ec_backend.h/cc - 纠删码后端实现
  • pg.h/cc - Placement Group主类
  • osd.h/cc - OSD主类

9.2 消息文件

  • messages/MOSDRepOp.h - 复制操作消息
  • messages/MOSDRepOpReply.h - 复制操作回复
  • messages/MOSDPGPCT.h - PG Committed-To更新

9.3 辅助文件

  • acked_peers.h - 已确认副本管理
  • shard_services.h - 分片服务
  • object_context.h - 对象上下文

Crimson 架构分析:BlueStore 上层逻辑 + SPDK 底层 IO 引擎

Crimson 架构分析:BlueStore 上层逻辑 + SPDK 底层 IO 引擎

1. 概述

本文档分析 Crimson(Ceph 的新实现)的存储架构,重点阐述 Crimson = BlueStore 的上层逻辑 + SPDK 的底层 IO 引擎 这一设计理念。

1.1 架构设计理念

Crimson 的存储架构采用了分层设计

  • 上层逻辑层:复用 BlueStore 的上层逻辑(元数据管理、空间分配、事务处理等)
  • 底层 IO 引擎层:使用 SPDK 作为高性能底层 IO 引擎

这种设计既保留了 BlueStore 经过验证的存储逻辑,又通过 SPDK 获得了用户态、零拷贝、轮询模式的高性能 IO 能力。

1.2 Crimson 存储架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
┌─────────────────────────────────────────────────────────┐
│ Crimson OSD (Seastar 异步框架) │
│ - 对象存储协议处理 │
│ - 事务管理 │
│ - 分布式一致性 │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ AlienStore (适配层) │
│ - 将 BlueStore 适配到 Seastar 异步模型 │
│ - ThreadPool: 在独立线程池中运行 BlueStore │
│ - Alien 机制: 桥接 Seastar shard 和 BlueStore 线程 │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ BlueStore (上层逻辑层) │
│ ├── 元数据管理 (Onode、Extent、Blob) │
│ ├── 空间分配器 (Allocator: AVL/Btree/Bitmap) │
│ ├── 空闲列表管理 (FreelistManager) │
│ ├── 压缩/去重 │
│ ├── RocksDB (元数据存储) │
│ │ └── BlueRocksEnv / SPDK RocksDB Environment │
│ │ └── SPDK BDEV (元数据 IO) │
│ └── BlockDevice (数据 IO) │
│ └── SPDK BDEV 或 KernelDevice │
│ └── SPDK NVMe Driver (底层 IO 引擎) │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ SPDK (底层 IO 引擎层) │
│ ├── SPDK BDEV (块设备抽象层) │
│ ├── SPDK NVMe Driver (用户态 NVMe 驱动) │
│ ├── SPDK RocksDB Environment (RocksDB IO 环境) │
│ └── DPDK 环境抽象 (env_dpdk) │
│ ├── 大页内存管理 │
│ ├── 轮询模式 I/O │
│ └── NUMA 感知 │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ NVMe SSD (物理存储设备) │
└─────────────────────────────────────────────────────────┘

2. Crimson 存储架构详解

2.1 AlienStore:适配层

位置: crimson/os/alienstore/

功能:将传统的 BlueStore(同步阻塞式)适配到 Seastar 异步框架

关键实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
AlienStore::AlienStore(const std::string& type,
const std::string& path,
const ConfigValues& values)
: type(type),
path{path},
values(values),
op_gates()
{
}

seastar::future<> AlienStore::start()
{
// 创建 CephContext
cct = std::make_unique<CephContext>(...);

// 创建底层的 ObjectStore(可以是 BlueStore)
store = ObjectStore::create(cct.get(), type, path);

// 创建独立线程池运行 BlueStore
// BlueStore 需要在非 Seastar 线程中运行
const auto num_threads =
get_conf<uint64_t>("crimson_bluestore_num_threads");
tp = std::make_unique<crimson::os::ThreadPool>(num_threads, 128, ...);
return tp->start();
}

工作原理

  1. ThreadPool:创建独立线程池,BlueStore 在此线程池中运行
  2. Alien 机制:使用 seastar::alien::submit_to() 将操作从 Seastar shard 提交到 BlueStore 线程
  3. 异步桥接:将 BlueStore 的同步回调转换为 Seastar future

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
AlienStore::read_errorator::future<ceph::bufferlist>
AlienStore::read(CollectionRef ch,
const ghobject_t& oid,
uint64_t offset,
size_t len,
uint32_t op_flags)
{
// 通过 ThreadPool 提交到 BlueStore 线程
return tp->submit(ch->get_cid().hash_to_shard(tp->size()), [=, this] {
// 在 BlueStore 线程中执行
auto c = static_cast<AlienCollection*>(ch.get());
return store->read(c->collection, oid, offset, len, bl, op_flags);
}).then([&bl] (int r) -> read_errorator::future<ceph::bufferlist> {
// 转换为 Seastar future
if (r == -ENOENT) {
return crimson::ct_error::enoent::make();
} else {
return read_errorator::make_ready_future<ceph::bufferlist>(std::move(bl));
}
});
}

2.2 BlueStore:上层逻辑层

位置: src/os/bluestore/

功能:提供对象存储的上层逻辑

主要组件

  1. 元数据管理

    • Onode:对象元数据
    • Extent:数据范围管理
    • Blob:数据块管理
  2. 空间分配器 (Allocator)

    • AvlAllocator:AVL 树分配器
    • BtreeAllocator:B+ 树分配器
    • BitmapAllocator:位图分配器
    • HybridAllocator:混合分配器
  3. 空闲列表管理 (FreelistManager)

    • 跟踪空闲空间
    • 持久化到 RocksDB
  4. RocksDB 元数据存储

    • BlueRocksEnv:自定义 RocksDB 环境
    • 可选:使用 SPDK RocksDB Environment(通过 SPDK BDEV)
  5. BlockDevice 数据 IO

    • KernelDevice:使用 Linux 内核驱动(传统方式)
    • 可选:使用 SPDK BDEV(通过 SPDK NVMe Driver)

BlueStore 编译到 Crimson

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
set(alien_store_srcs
alien_store.cc
thread_pool.cc
alien_log.cc
${PROJECT_SOURCE_DIR}/src/os/ObjectStore.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/Allocator.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/AllocatorBase.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/AvlAllocator.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/BtreeAllocator.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/Btree2Allocator.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/BitmapFreelistManager.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/BlueFS.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/bluefs_types.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/BlueRocksEnv.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/BlueStore.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/simple_bitmap.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/bluestore_types.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/fastbmap_allocator_impl.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/FreelistManager.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/HybridAllocator.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/StupidAllocator.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/BitmapAllocator.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/Writer.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/Compression.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/BlueStore_debug.cc
${PROJECT_SOURCE_DIR}/src/os/bluestore/BlueAdmin.cc

2.3 SPDK:底层 IO 引擎层

位置: src/spdk/

功能:提供高性能底层 IO 引擎

主要组件

  1. SPDK RocksDB Environment (lib/rocksdb/env_spdk.cc)

    • 为 RocksDB 提供 SPDK BDEV 后端
    • 元数据 IO 通过 SPDK 执行
  2. SPDK BDEV (lib/bdev/)

    • 统一的块设备抽象
    • 支持 NVMe、AIO、Malloc 等后端
  3. SPDK NVMe Driver (lib/nvme/)

    • 用户态 NVMe 驱动
    • 轮询模式、零拷贝、低延迟

SPDK 与 BlueStore 的集成方式

方式 1:RocksDB 元数据 IO(通过 SPDK RocksDB Environment)

1
2
3
4
5
6
7
BlueStore
└── RocksDB (元数据存储)
└── BlueRocksEnv
└── SPDK RocksDB Environment
└── SPDK BDEV
└── SPDK NVMe Driver
└── NVMe SSD

说明

  • BlueStore 的 RocksDB 元数据可以通过 SPDK RocksDB Environment 使用 SPDK BDEV
  • 元数据的读写操作通过 SPDK 执行,获得高性能

方式 2:数据 IO(通过 SPDK BDEV)

1
2
3
4
5
6
BlueStore
└── BlockDevice
└── SPDK BDEV 适配器(需要实现)
└── SPDK BDEV
└── SPDK NVMe Driver
└── NVMe SSD

说明

  • 数据 IO 可以通过 SPDK BDEV 执行
  • 需要实现 BlockDevice 到 SPDK BDEV 的适配层

3. Crimson 与 SPDK 的集成方式

3.1 集成架构总结

根据 Crimson = BlueStore 的上层逻辑 + SPDK 的底层 IO 引擎 这一设计:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
┌─────────────────────────────────────────────────────────┐
│ Crimson OSD (Seastar) │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ AlienStore (适配层) │
│ - ThreadPool: 运行 BlueStore │
│ - Alien 机制: 桥接 Seastar 和 BlueStore │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ BlueStore (上层逻辑层) │
│ ├── 元数据管理 (Onode/Extent/Blob) │
│ ├── 空间分配器 (Allocator) │
│ ├── RocksDB (元数据) │
│ │ └── SPDK RocksDB Environment ───┐ │
│ │ │ │
│ └── BlockDevice (数据) │ │
│ └── SPDK BDEV 适配器 ────────────┼──→ SPDK 层 │
│ │ │
└──────────────────────────────────────┼───────────────────┘

┌──────────────────────┐
│ SPDK (IO 引擎层) │
├──────────────────────┤
│ - SPDK BDEV │
│ - SPDK NVMe Driver │
│ - DPDK 环境抽象 │
└──────────────────────┘

┌──────────────────────┐
│ NVMe SSD │
└──────────────────────┘

3.2 当前集成状态

已集成

  1. AlienStore 使用 BlueStore:通过 ObjectStore::create() 创建 BlueStore 实例
  2. BlueStore 逻辑复用:完整使用 BlueStore 的上层逻辑(分配器、元数据管理等)
  3. SPDK RocksDB Environment:SPDK 提供 RocksDB 环境实现,可供 BlueStore 使用

可选集成

  1. SPDK BDEV 用于数据 IO:需要实现 BlockDevice 到 SPDK BDEV 的适配层
  2. 完整 SPDK 集成:元数据和数据 IO 都通过 SPDK 执行

4. 技术实现细节

4.1 线程模型适配

问题

  • Seastar:每个 shard 一个线程,协作式多任务
  • BlueStore:同步阻塞式,需要在独立线程中运行
  • SPDK:轮询模式,需要在 DPDK 管理的线程中运行

解决方案

  1. AlienStore ThreadPool

    • BlueStore 在独立线程池中运行
    • 通过 tp->submit() 提交操作
  2. Alien 机制

    • 使用 seastar::alien::submit_to() 在非 Seastar 线程中执行代码
    • 可以用于 SPDK 操作
  3. SPDK 线程

    • SPDK 在独立线程中运行(通过 DPDK lcore)
    • 可以通过 Alien 机制访问

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 在 Seastar shard 中调用
seastar::future<> crimson_operation() {
// 提交到 BlueStore 线程
return tp->submit([=] {
// BlueStore 操作
store->read(...);
}).then([] (int r) {
// 返回 Seastar future
});
}

// 如果使用 SPDK,可以通过 Alien 访问
seastar::future<> spdk_operation() {
return seastar::alien::submit_to(
spdk_thread_id, // SPDK 线程 ID
[] {
// SPDK 操作
spdk_bdev_read(...);
});
}

4.2 内存模型适配

问题

  • Seastar:标准 C++ 内存分配
  • BlueStore:标准 C++ 内存分配
  • SPDK:大页内存、NUMA 感知、IOVA

解决方案

  1. BlueStore ↔ Seastar

    • 使用标准内存,在边界处传递数据
    • ThreadPool 保证内存访问安全
  2. BlueStore ↔ SPDK

    • 选项 1:使用 SPDK 内存分配(性能更好)
    • 选项 2:在边界处拷贝数据(简单但开销)

4.3 异步模型适配

问题

  • Seastar:future/promise 异步模型
  • BlueStore:同步阻塞 + 回调
  • SPDK:回调或轮询

解决方案

  1. BlueStore 操作 → Seastar future

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // AlienStore 将 BlueStore 同步操作包装为 future
    seastar::future<result> crimson_read(...) {
    return tp->submit([=] {
    // 同步阻塞操作
    return store->read(...);
    }).then([] (int r) {
    // 转换为 future
    return seastar::make_ready_future<result>(...);
    });
    }
  2. SPDK 操作 → Seastar future

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    seastar::future<> spdk_read(...) {
    return seastar::alien::submit_to(spdk_thread_id, [=] {
    auto promise = seastar::make_promise<>();
    spdk_bdev_read(...,
    [](void* ctx, bool success) {
    auto p = static_cast<seastar::promise<>*>(ctx);
    p->set_value();
    }, promise.get());
    return promise.get_future();
    });
    }

5. 性能优势

5.1 使用 SPDK 的优势

  1. 用户态驱动

    • 避免内核上下文切换
    • 减少系统调用开销
  2. 轮询模式

    • 零延迟响应
    • 可预测的性能
  3. 零拷贝

    • 直接 DMA 访问
    • 减少数据拷贝
  4. 大页内存

    • 减少 TLB miss
    • 提高内存访问效率

5.2 架构优势

  1. 复用成熟逻辑

    • BlueStore 的上层逻辑经过验证
    • 减少开发和测试成本
  2. 性能优化

    • SPDK 提供高性能底层 IO
    • 最佳的性能组合
  3. 灵活性

    • 可以逐步迁移到 SPDK
    • 支持混合模式(部分使用 SPDK)

6. 未来发展方向

6.1 完整 SPDK 集成

目标

  • 元数据 IO 使用 SPDK RocksDB Environment
  • 数据 IO 使用 SPDK BDEV
  • 完整的 SPDK 集成方案

挑战

  • BlockDevice 到 SPDK BDEV 的适配层实现
  • 内存模型统一
  • 性能测试和优化

6.2 SeaStore vs AlienStore

当前状态

  • SeaStore:Crimson 原生的存储后端(不使用 BlueStore)
  • AlienStore:使用 BlueStore 的适配层

未来方向

  • SeaStore 可能成为 Crimson 的默认存储后端
  • AlienStore 作为过渡方案或兼容性选项

7. 可能的集成方式(传统方式作为参考)

3.1 方案一:通过 SPDK BDEV 抽象层

架构

1
2
3
4
5
6
7
8
9
10
11
Crimson SeaStore

RBMDevice 抽象

SPDK BDEV 接口适配层 (需要实现)

SPDK BDEV (如 NVMe BDEV)

SPDK NVMe Driver

NVMe SSD

实现要点

  1. 创建 SPDK BDEV 适配层

    1
    2
    3
    4
    class SPDKBDevAdapter : public RBMDevice {
    // 将 SPDK BDEV 的 C API 适配为 Seastar future
    // 需要桥接 SPDK 的轮询模式和 Seastar 的异步模型
    };
  2. 线程模型适配

    • SPDK 在独立线程中运行
    • 通过消息队列与 Seastar shard 通信
    • 使用 Seastar 的 alien 机制(已在 Crimson 中存在)
  3. 内存模型适配

    • SPDK 使用大页内存
    • 需要数据拷贝或共享内存

3.2 方案二:使用 SPDK 的 RocksDB 环境

架构

1
2
3
4
5
6
7
8
9
10
11
Crimson SeaStore

RocksDB (可选,用于元数据)

SPDK RocksDB Environment

SPDK BDEV

SPDK NVMe Driver

NVMe SSD

说明

  • SPDK 提供了 RocksDB 环境实现(lib/rocksdb/env_spdk.cc
  • 可以在 RocksDB 层面使用 SPDK
  • 但 SeaStore 主要不是基于 RocksDB

3.3 方案三:直接使用 SPDK NVMe API

架构

1
2
3
4
5
6
7
Crimson SeaStore

NVMeBlockDevice (修改为使用 SPDK)

SPDK NVMe API (spdk_nvme_*)

NVMe SSD

实现要点

  1. 在独立线程中初始化 SPDK

    1
    2
    // SPDK 需要在非 Seastar 线程中运行
    // 使用 alien 线程运行 SPDK
  2. 桥接 SPDK 和 Seastar

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // SPDK I/O 完成回调 → Seastar promise
    class SPDKNVMebridge {
    seastar::promise<> io_promise;

    static void spdk_io_complete(void* ctx, bool success) {
    auto bridge = static_cast<SPDKNVMebridge*>(ctx);
    // 转换为 Seastar future
    bridge->io_promise.set_value();
    }
    };
  3. 内存管理

    1
    2
    3
    // 使用 SPDK 内存池
    void* spdk_buffer = spdk_malloc(size, ...);
    // 或使用 Seastar 内存,但需要确保兼容性

4. 集成挑战

4.1 线程模型冲突

问题

  • SPDK 期望在 DPDK 管理的线程中运行
  • Seastar 有自己的线程模型(每个 shard 一个线程)

解决方案

  • 使用 alien 机制在独立线程中运行 SPDK
  • 通过消息传递与 Seastar shard 通信

4.2 异步模型差异

问题

  • SPDK 使用回调或轮询
  • Seastar 使用 future/promise

解决方案

  • 实现适配层,将 SPDK 回调转换为 Seastar future
  • 示例:
1
2
3
4
5
6
7
8
9
10
11
12
seastar::future<> spdk_read_async(...) {
return seastar::alien::submit_to(
spdk_thread_id, [=] {
auto promise = seastar::make_promise<>();
spdk_nvme_ns_cmd_read(...,
[](void* ctx, bool success) {
auto p = static_cast<promise*>(ctx);
p->set_value();
}, promise.get());
return promise.get_future();
});
}

4.3 内存模型差异

问题

  • SPDK 使用大页内存、IOVA
  • Seastar 使用标准内存分配

解决方案

  • 选项 1:使用 SPDK 内存分配(性能更好)
  • 选项 2:在边界处拷贝数据(简单但性能开销)

5. 实际代码示例(假设集成 SPDK)

如果要在 Crimson 中集成 SPDK,代码可能如下:

5.1 SPDK BDEV 适配器(伪代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class SPDKBDevAdapter : public RBMDevice {
struct spdk_bdev* bdev;
struct spdk_bdev_desc* bdev_desc;
struct spdk_io_channel* io_channel;

// SPDK 运行在独立线程中
uint32_t spdk_core_id;

public:
// 初始化 SPDK 环境
seastar::future<> init_spdk() {
return seastar::alien::submit_to(spdk_core_id, [] {
// SPDK 初始化
spdk_env_init(&opts);
spdk_bdev_initialize();

// 打开 BDEV
bdev = spdk_bdev_get_by_name(device_name);
spdk_bdev_open(bdev, true, ...);
io_channel = spdk_bdev_get_io_channel(bdev_desc);
});
}

// 读操作
read_ertr::future<> read(uint64_t offset, bufferptr& bptr) {
return seastar::alien::submit_to(spdk_core_id, [=] {
auto promise = seastar::make_promise<>();

// 分配 SPDK 内存
void* buf = spdk_malloc(length, ...);

// 提交 SPDK I/O
int rc = spdk_bdev_read(bdev_desc, io_channel, buf,
offset, length,
[](struct spdk_bdev_io* bdev_io,
bool success, void* ctx) {
auto p = static_cast<promise*>(ctx);
spdk_bdev_free_io(bdev_io);
// 拷贝数据到 Seastar buffer
// ...
p->set_value();
}, promise.get());

return promise.get_future();
});
}
};

5.2 Alien 线程通信

Crimson 已经支持 alien 机制(用于在非 Seastar 线程中执行代码):

1
2
3
4
5
6
7
8
9
// 在 alien 线程中运行 SPDK
seastar::future<> spdk_operation() {
return seastar::alien::submit_to(
alien_thread_id, // SPDK 运行的线程
[] {
// SPDK 操作
spdk_nvme_ns_cmd_read(...);
});
}

6. 性能考虑

6.1 使用 SPDK 的优势

  1. 用户态驱动:避免内核上下文切换
  2. 轮询模式:零延迟响应
  3. 零拷贝:直接 DMA 访问

6.2 集成开销

  1. 线程间通信:alien 调用有开销
  2. 内存拷贝:如果使用不同的内存模型
  3. 上下文切换:虽然减少内核切换,但增加了用户态切换

6.3 适用场景

SPDK 集成可能在以下场景更有价值:

  1. 高频 I/O:大量小 I/O 操作
  2. 延迟敏感:需要极致延迟的场景
  3. CPU 充足:可以接受轮询模式的高 CPU 占用

7. 总结

7.1 架构总结

Crimson = BlueStore 的上层逻辑 + SPDK 的底层 IO 引擎

  • 上层逻辑层:使用 BlueStore 的成熟逻辑(元数据管理、空间分配、事务处理等)
  • 适配层:AlienStore 将 BlueStore 适配到 Seastar 异步框架
  • 底层 IO 引擎层:SPDK 提供高性能底层 IO(用户态驱动、轮询模式、零拷贝)

7.2 当前状态

已实现

  • ✅ AlienStore 使用 BlueStore 的上层逻辑
  • ✅ BlueStore 的完整功能在 Crimson 中可用
  • ✅ SPDK 提供了 RocksDB Environment,可供 BlueStore 使用

可选集成

  • ⚠️ SPDK RocksDB Environment 用于元数据 IO(可选配置)
  • ⚠️ SPDK BDEV 用于数据 IO(需要适配层)

7.3 架构优势

  1. 复用成熟逻辑

    • BlueStore 的上层逻辑经过大规模验证
    • 减少开发和测试成本
  2. 高性能底层 IO

    • SPDK 提供用户态驱动、轮询模式、零拷贝
    • 可以获得极致的 IO 性能
  3. 灵活性

    • 可以逐步集成 SPDK
    • 支持混合模式(部分使用 SPDK)
  4. 渐进式迁移

    • 现有 BlueStore 部署可以平滑迁移到 Crimson
    • 通过 AlienStore 实现兼容

7.4 技术要点

  1. 线程模型适配

    • AlienStore ThreadPool:BlueStore 在独立线程池中运行
    • Alien 机制:桥接 Seastar shard 和 BlueStore/SPDK 线程
  2. 异步模型适配

    • 将 BlueStore 同步操作包装为 Seastar future
    • 将 SPDK 回调转换为 Seastar future
  3. 内存模型适配

    • Seastar/BlueStore:标准 C++ 内存
    • SPDK:大页内存、NUMA 感知
    • 在边界处处理内存兼容性

7.5 未来方向

  1. 完整 SPDK 集成

    • 实现 BlockDevice 到 SPDK BDEV 的适配层
    • 元数据和数据 IO 都通过 SPDK 执行
  2. 性能优化

    • 减少线程间通信开销
    • 优化内存拷贝
    • 性能测试和调优
  3. SeaStore 发展

    • SeaStore 可能成为 Crimson 的默认存储后端
    • AlienStore 作为过渡方案或兼容性选项

7.4 参考资料

  • SPDK NVMe API: spdk/include/spdk/nvme.h
  • SPDK BDEV API: spdk/include/spdk/bdev.h
  • Crimson NVMe 实现: crimson/os/seastore/random_block_manager/nvme_block_device.*
  • Seastar Alien: Seastar 框架的 alien 机制文档

SeaStore 架构分析与流程

SeaStore 架构分析与流程

1. 概述

SeaStore 是 Ceph Crimson OSD 的存储引擎实现,基于 Seastar 框架构建。它采用日志结构存储(Log-Structured Storage)设计,提供高性能的异步存储操作。

1.1 核心特性

  • 日志结构存储:所有写入都追加到日志中,提供顺序写入性能
  • 事务性操作:支持 ACID 特性的事务处理
  • 多 Shard 架构:每个 CPU 核心独立处理 I/O,充分利用多核性能
  • 异步清理机制:后台自动回收和整理存储空间
  • 多设备支持:支持段式设备(Segment)和随机块设备(Random Block)

2. 架构层次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
┌─────────────────────────────────────────────────────────┐
│ SeaStore (API层) │
│ - 实现 FuturizedStore 接口 │
│ - 提供对象存储操作 (read/write/omap/attr) │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ TransactionManager (事务管理层) │
│ - 事务创建和管理 │
│ - 逻辑地址到物理地址映射 (LBA) │
│ - Extent 分配和读取 │
└─────────────────────────────────────────────────────────┘

┌───────────────────┴───────────────────┐
│ │
┌───────────────────┐ ┌──────────────────────┐
│ Cache (缓存层) │ │ Journal (日志层) │
│ - Extent 缓存 │ │ - 事务记录写入 │
│ - 事务状态管理 │ │ - 日志回放 │
│ - 脏页管理 │ │ - 日志裁剪 │
└───────────────────┘ └──────────────────────┘
│ │
└───────────────────┬───────────────────┘

┌─────────────────────────────────────────────────────────┐
│ ExtentPlacementManager (放置管理层) │
│ - Extent 物理地址分配 │
│ - 段管理和分配 │
└─────────────────────────────────────────────────────────┘

┌───────────────────┴───────────────────┐
│ │
┌───────────────────┐ ┌──────────────────────┐
│ SegmentManager │ │ RandomBlockManager │
│ (段式设备) │ │ (随机块设备) │
└───────────────────┘ └──────────────────────┘

3. 核心组件详解

3.1 SeaStore

位置: seastore.h, seastore.cc

职责:

  • 实现 FuturizedStore 接口,提供对象存储操作
  • 管理多个 Shard,每个 Shard 处理一个 CPU 核心的 I/O
  • 提供对象数据、OMAP、属性等存储功能

关键方法:

1
2
3
4
5
6
7
8
9
10
class SeaStore::Shard {
// 对象操作
seastar::future<struct stat> stat(CollectionRef, const ghobject_t&);
read_errorator::future<ceph::bufferlist> read(...);
seastar::future<> do_transaction_no_callbacks(...);

// 集合操作
seastar::future<CollectionRef> create_new_collection(...);
seastar::future<CollectionRef> open_collection(...);
}

3.2 TransactionManager

位置: transaction_manager.h, transaction_manager.cc

职责:

  • 管理事务生命周期
  • 提供逻辑地址到物理地址的映射(通过 LBA Manager)
  • 管理 Extent 的分配和读取
  • 提交事务到 Journal

关键方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class TransactionManager {
// 事务创建
TransactionRef create_transaction(Transaction::src_t, const char*, ...);

// Extent 操作
template<typename T>
read_extent_ret<T> read_extent(Transaction&, laddr_t, extent_len_t);

template<typename T>
alloc_extent_ret<T> alloc_non_data_extent(...);

// 事务提交
submit_transaction_iertr::future<> submit_transaction(Transaction&);
}

3.3 Cache

位置: cache.h, cache.cc

职责:

  • 管理 Extent 缓存
  • 处理事务的 read_set、write_set、retired_set
  • 提供 Extent 的读取和分配
  • 管理脏页列表

事务三阶段:

  1. 构造阶段: 用户调用 Cache::create_transaction() 并填充事务
  2. 提交阶段: 用户调用 Cache::try_start_transaction(),如果成功则提交到 Journal
  3. 完成阶段: 事务持久化后,调用 Cache::complete_commit()

关键数据结构:

1
2
3
4
5
class Cache {
ExtentIndex extents_index; // 所有缓存的 extent
CachedExtent::primary_ref_list dirty; // 脏页列表
RootBlockRef root; // 根块引用
}

3.4 Journal

位置: journal.h, journal/segmented_journal.cc

职责:

  • 将事务记录写入持久化存储
  • 提供日志回放功能
  • 管理日志空间和裁剪

关键方法:

1
2
3
4
5
6
7
8
9
10
11
class Journal {
// 提交记录
submit_record_ertr::future<> submit_record(
record_t&&, OrderingHandle&, ...);

// 日志回放
replay_ret replay(delta_handler_t&&);

// 刷新
seastar::future<> flush(OrderingHandle&);
}

3.5 SegmentManager

位置: segment_manager.h, segment_manager.cc

职责:

  • 管理段式存储设备
  • 提供段的分配、释放、写入操作
  • 跟踪段的状态(EMPTY/OPEN/CLOSED)

段状态:

  • EMPTY: 空段,可以分配
  • OPEN: 正在写入的段
  • CLOSED: 已关闭的段,等待清理

3.6 OnodeManager

位置: onode_manager.h, onode_manager/staged-fltree/fltree_onode_manager.cc

职责:

  • 管理对象节点(Onode)的创建、查找、删除
  • 维护 Onode 树结构
  • 提供对象列表功能

关键方法:

1
2
3
4
5
6
class OnodeManager {
get_onode_ret get_onode(Transaction&, const ghobject_t&);
get_or_create_onode_ret get_or_create_onode(...);
erase_onode_ret erase_onode(Transaction&, OnodeRef&);
list_onodes_ret list_onodes(...);
}

3.7 LBAManager

位置: lba_manager.h, lba/btree_lba_manager.cc

职责:

  • 管理逻辑地址(LBA)到物理地址(PBA)的映射
  • 提供 B+ 树结构存储映射关系
  • 支持间接映射(用于克隆操作)

关键概念:

  • 直接映射: LBA 直接映射到物理地址
  • 间接映射: LBA 映射到另一个 LBA,用于实现写时复制(COW)

3.8 AsyncCleaner

位置: async_cleaner.h, async_cleaner.cc

职责:

  • 后台清理已关闭的段
  • 回收无效数据占用的空间
  • 管理段的空间利用率

清理策略:

  • 基于段的利用率选择清理目标
  • 支持多种 GC 算法(GREEDY, BENEFIT, COST_BENEFIT)

4. 数据流程

4.1 写入流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
用户写入请求


SeaStore::Shard::do_transaction_no_callbacks()


解析 Transaction 操作


TransactionManager::submit_transaction()

├─► Cache::prepare_record() // 准备记录

├─► Journal::submit_record() // 写入日志

├─► ExtentPlacementManager::dispatch() // 分配物理地址

└─► Cache::complete_commit() // 完成提交

详细步骤:

  1. 事务创建: TransactionManager::create_transaction()

    • 创建新的事务对象
    • 初始化事务 ID 和状态
  2. 操作执行: 在事务中执行各种操作

    • _write(): 写入对象数据
    • _setattrs(): 设置属性
    • _omap_set_keys(): 设置 OMAP 键值
  3. 事务提交: TransactionManager::submit_transaction()

    • 准备记录: Cache::prepare_record()
      • 收集所有 dirty extents
      • 构建 record_t 结构
    • 提交到 Journal: Journal::submit_record()
      • 写入日志段
      • 等待持久化完成
    • 完成提交: Cache::complete_commit()
      • 更新 extent 的物理地址
      • 标记事务完成

4.2 读取流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
用户读取请求


SeaStore::Shard::read()


TransactionManager::with_transaction_intr()

├─► OnodeManager::get_onode() // 获取对象节点

├─► ObjectDataHandler::read() // 读取对象数据

├─► TransactionManager::read_extent() // 读取 extent

├─► LBAManager::get_mapping() // 获取 LBA 映射

└─► Cache::get_extent_if_cached() // 从缓存获取或读取磁盘

详细步骤:

  1. 创建读事务: TransactionManager::create_transaction(READ)

  2. 获取 Onode: OnodeManager::get_onode()

    • 从 Onode 树中查找对象
    • 返回 Onode 引用
  3. 读取数据: ObjectDataHandler::read()

    • 根据 Onode 中的 LBA 地址读取数据
    • 通过 TransactionManager::read_extent() 读取 extent
  4. LBA 映射: LBAManager::get_mapping()

    • 查找逻辑地址对应的物理地址
    • 处理间接映射
  5. 缓存查找: Cache::get_extent_if_cached()

    • 首先在事务的 read_set 中查找
    • 然后在 Cache 的 extents_index 中查找
    • 如果未命中,从磁盘读取

4.3 事务冲突处理

SeaStore 使用乐观并发控制(OCC)处理事务冲突:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
事务提交时


检查 read_set 中所有 extent 是否仍然有效

├─► 全部有效 → 提交成功

└─► 有无效 extent → 事务冲突


返回 eagain


用户重试事务

冲突检测:

  • 每个 extent 维护一个版本号或序列号
  • 事务提交时检查 read_set 中所有 extent 的版本
  • 如果版本发生变化,说明发生了冲突

4.4 日志回放流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
系统启动


Journal::replay()


读取日志记录

├─► 对于每个 delta:
│ │
│ ├─► Cache::replay_delta()
│ │
│ ├─► 读取相关 extent
│ │
│ └─► 应用 delta 变更

└─► 重建 Cache 状态

回放步骤:

  1. 扫描日志: 从日志头开始扫描所有记录

  2. 应用 Delta: 对于每个 delta

    • 读取对应的 extent
    • 应用 delta 变更
    • 标记 extent 为 dirty
  3. 重建索引:

    • 重建 LBA 树
    • 重建 Onode 树
    • 重建其他元数据结构
  4. 验证一致性: 检查所有数据结构的一致性

4.5 清理流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
后台清理任务


AsyncCleaner::clean_space()

├─► 选择要清理的段
│ │
│ └─► 基于利用率、时间等策略

├─► 扫描段中的有效 extent
│ │
│ └─► BackrefManager::retrieve_backref_extents_in_range()

├─► 重写有效 extent 到新段
│ │
│ └─► TransactionManager::rewrite_extent()

└─► 释放旧段空间

清理策略:

  1. 段选择:

    • 计算每个段的利用率
    • 选择利用率低的段进行清理
  2. Extent 扫描:

    • 使用 BackrefManager 查找段中的有效 extent
    • 确定哪些 extent 仍然被引用
  3. 重写:

    • 将有效 extent 写入新段
    • 更新 LBA 映射
    • 提交事务
  4. 释放:

    • 标记旧段为空
    • 更新空间跟踪器

5. 关键数据结构

5.1 Transaction

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Transaction {
// 事务集合
read_set_t read_set; // 读取的 extent
write_set_t write_set; // 写入的 extent
retired_set_t retired_set; // 废弃的 extent

// 块列表
fresh_block_list_t fresh_block_list; // 新分配的块
mutated_block_list_t mutated_block_list; // 修改的块

// 元数据
transaction_id_t trans_id; // 事务 ID
Transaction::src_t src; // 事务来源
OrderingHandleRef handle; // 排序句柄
}

5.2 CachedExtent

1
2
3
4
5
6
7
8
9
10
11
12
13
class CachedExtent {
// 状态
extent_state_t state; // CLEAN/DIRTY/PENDING
paddr_t paddr; // 物理地址
laddr_t laddr; // 逻辑地址(如果是逻辑 extent)

// 版本控制
extent_version_t version; // 版本号
journal_seq_t dirty_from; // 变脏的日志序列号

// 引用计数
read_transaction_set_t read_transactions; // 读取事务集合
}

5.3 LBAMapping

1
2
3
4
5
6
7
8
struct LBAMapping {
laddr_t key; // 逻辑地址
paddr_t val; // 物理地址
extent_len_t length; // 长度
extent_ref_count_t refcount; // 引用计数
bool is_indirect; // 是否为间接映射
checksum_t checksum; // 校验和
}

5.4 record_t

1
2
3
4
5
6
7
struct record_t {
journal_seq_t seq; // 日志序列号
std::vector<CachedExtentRef> extents; // Extent 列表
std::vector<delta_info_t> deltas; // Delta 列表
extent_len_t dlength; // 数据长度
extent_len_t mlength; // 元数据长度
}

6. 地址空间

6.1 逻辑地址 (LBA)

  • 范围: L_ADDR_MINL_ADDR_MAX
  • 用途: 用户可见的地址空间
  • 管理: 由 LBAManager 通过 B+ 树管理

6.2 物理地址 (PBA)

SeaStore 支持两种物理地址类型:

  1. 段地址 (Segment Address):

    • paddr_t::make_seg_paddr(segment_id, offset)
    • 用于段式设备
  2. 块地址 (Block Address):

    • paddr_t::make_blk_paddr(device_id, offset)
    • 用于随机块设备

6.3 地址转换

1
2
3
4
5
6
7
8
逻辑地址 (LBA)


LBAManager::get_mapping()

├─► 直接映射 → 物理地址

└─► 间接映射 → 中间 LBA → 物理地址

7. 并发控制

7.1 Shard 隔离

  • 每个 CPU 核心运行一个独立的 Shard
  • Shard 之间通过消息传递通信
  • 每个 Shard 有独立的 Cache 和 TransactionManager

7.2 事务隔离

  • 读事务: 可以并发执行
  • 写事务: 通过 OrderingHandle 保证顺序
  • 冲突检测: 使用乐观并发控制

7.3 锁机制

  • Extent 锁: 每个 extent 维护读事务集合
  • 段锁: 段写入时加锁
  • 集合锁: SeastoreCollection::ordering_lock 保证操作顺序

8. 性能优化

8.1 缓存策略

  • LRU 缓存: 使用 ExtentPinboard 管理缓存优先级
  • 部分读取: 支持 extent 的部分范围读取
  • 预取: 可以预取相关 extent

8.2 写入优化

  • 批量提交: 多个操作合并到一个事务
  • 顺序写入: 日志结构保证顺序写入性能
  • 异步提交: 写入操作异步完成

8.3 清理优化

  • 增量清理: 每次只清理部分段
  • 智能选择: 基于利用率选择清理目标
  • 并行清理: 可以并行处理多个段

9. 错误处理

9.1 错误类型

  • input_output_error: I/O 错误
  • enospc: 空间不足
  • eagain: 事务冲突,需要重试
  • enoent: 对象不存在
  • value_too_large: 值过大

9.2 错误恢复

  • 事务回滚: 冲突时自动回滚
  • 日志回放: 系统重启时从日志恢复
  • 一致性检查: 定期检查数据结构一致性

10. 总结

SeaStore 是一个高性能的日志结构存储引擎,具有以下特点:

  1. 架构清晰: 分层设计,职责明确
  2. 高性能: 充分利用多核和异步 I/O
  3. 可靠性: 事务性保证和数据一致性
  4. 可扩展: 支持多种存储设备和配置

通过日志结构存储和异步清理机制,SeaStore 在保证数据一致性的同时,提供了优异的写入性能。

SeaStore 物理盘接口与底层库

SeaStore 物理盘接口与底层库

1. 概述

SeaStore 支持多种物理存储设备类型,通过统一的设备抽象接口访问底层存储。所有设备操作最终都基于 Seastar 框架 的异步 I/O 接口实现。

2. 支持的设备类型

2.1 设备类型枚举

位置: seastore_types.h:957

1
2
3
4
5
6
7
8
9
10
11
enum class device_type_t : uint8_t {
NONE = 0,
HDD, // 传统机械硬盘
SSD, // 固态硬盘(段式)
ZBD, // Zoned Block Device (ZNS SSD 或 SMR HDD)
EPHEMERAL_COLD, // 临时设备(冷数据,测试用)
EPHEMERAL_MAIN, // 临时设备(主数据,测试用)
RANDOM_BLOCK_SSD, // 随机块访问 SSD
RANDOM_BLOCK_EPHEMERAL, // 临时随机块设备(测试用)
NUM_TYPES
};

2.2 后端类型分类

设备类型按后端实现分为两类:

1
2
3
4
enum class backend_type_t : uint8_t {
SEGMENTED, // 段式设备:SSD, ZBD, HDD
RANDOM_BLOCK // 随机块设备:RANDOM_BLOCK_SSD
};

映射关系:

  • SEGMENTED: HDD, SSD, ZBD, EPHEMERAL_MAIN, EPHEMERAL_COLD
  • RANDOM_BLOCK: RANDOM_BLOCK_SSD, RANDOM_BLOCK_EPHEMERAL

3. 设备实现与底层库

3.1 段式设备 (SEGMENTED)

3.1.1 BlockSegmentManager (SSD/HDD)

位置: segment_manager/block.h, segment_manager/block.cc

底层库: Seastar DMA 接口

实现方式:

  • 使用 seastar::file::dma_read()dma_write() 进行 I/O
  • 通过 seastar::open_file_dma() 打开设备文件
  • 支持批量 I/O (writev/readv)

关键代码:

1
2
3
4
5
6
7
8
9
10
11
// 打开设备
seastar::open_file_dma(
path,
seastar::open_flags::rw | seastar::open_flags::dsync
)

// 写入
device.dma_write(offset, buffer, length)

// 读取
device.dma_read(offset, buffer, length)

系统调用:

  • Seastar 的 DMA 接口最终调用 Linux AIO (io_submit/io_getevents) 或 io_uring

3.1.2 ZBDSegmentManager (ZBD)

位置: segment_manager/zbd.h, segment_manager/zbd.cc

底层库:

  • Seastar DMA 接口 (基础 I/O)
  • Linux ZBD 接口 (linux/blkzoned.h)

实现方式:

  • 使用 Seastar DMA 进行数据读写
  • 使用 Linux ioctl 进行 Zone 管理操作

Zone 操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <linux/blkzoned.h>

enum class zone_op {
OPEN, // 打开 Zone
FINISH, // 完成 Zone
CLOSE, // 关闭 Zone
RESET // 重置 Zone
};

// 通过 ioctl 操作 Zone
device.ioctl(BLKOPENZONE, ...) // 打开 Zone
device.ioctl(BLKCLOSEZONE, ...) // 关闭 Zone
device.ioctl(BLKFINISHZONE, ...) // 完成 Zone
device.ioctl(BLKRESETZONE, ...) // 重置 Zone
device.ioctl(BLKGETNRZONES, ...) // 获取 Zone 数量

支持的设备:

  • ZNS SSD (NVMe Zoned Namespaces)
  • SMR HDD (Shingled Magnetic Recording)

3.1.3 EphemeralSegmentManager (临时设备)

位置: segment_manager/ephemeral.h, segment_manager/ephemeral.cc

底层库: 内存映射 (用于测试)

实现方式:

  • 使用内存作为存储介质
  • 不涉及实际物理设备

3.2 随机块设备 (RANDOM_BLOCK)

3.2.1 NVMeBlockDevice (RANDOM_BLOCK_SSD)

位置: random_block_manager/nvme_block_device.h, nvme_block_device.cc

底层库:

  • Seastar DMA 接口 (基础 I/O)
  • Linux NVMe ioctl (linux/nvme_ioctl.h)

实现方式:

  • 使用 Seastar DMA 进行常规读写
  • 使用 Linux NVMe ioctl 进行设备特性查询和管理

NVMe 特性支持:

  1. Identify 命令:
1
2
3
4
5
// 识别控制器
co_await identify_controller(device);

// 识别命名空间
co_await identify_namespace(device);
  1. 多流写入 (Multi-Stream):
1
2
3
4
5
// 支持多流写入,通过 stream 参数区分数据生命周期
write_ertr::future<> write(
uint64_t offset,
bufferptr bptr,
uint16_t stream = 0);
  1. 端到端数据保护 (E2E Data Protection):
1
2
3
4
5
6
7
// 检查是否支持端到端数据保护
bool is_end_to_end_data_protection() const;

// 如果支持,使用 NVMe 原生写入
if (is_end_to_end_data_protection()) {
co_await nvme_write(offset, length, buffer);
}
  1. NVMe Passthrough:
1
2
3
4
5
6
7
// 传递 IO 命令
nvme_command_ertr::future<> pass_through_io(
nvme_io_command_t &io_cmd);

// 传递管理命令
nvme_command_ertr::future<> pass_admin(
nvme_admin_command_t &admin_cmd);

关键代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 打开设备
auto file = co_await seastar::open_file_dma(in_path, mode);
device = std::move(file);

// 识别设备特性
auto id_ctr_data = co_await identify_controller(device);
auto id_ns_data = co_await identify_namespace(device);

// 写入(支持多流)
co_await io_device[stream].dma_write(offset, buffer, length);

// NVMe 原生写入(用于 E2E 保护)
co_await device.ioctl(NVME_IOCTL_IO_CMD, &io_cmd);

3.2.2 EphemeralRBMDevice (临时随机块设备)

位置: random_block_manager/rbm_device.h

底层库: 内存映射 (用于测试)

实现方式:

  • 使用 mmap 分配内存
  • 模拟随机块设备行为

4. 底层库详解

4.1 Seastar 框架

核心库: Seastar (https://github.com/scylladb/seastar)

主要接口:

  1. 文件操作:
1
2
3
4
5
6
7
8
9
10
11
// 打开文件(DMA 模式)
seastar::future<seastar::file> open_file_dma(
const std::string& path,
seastar::open_flags mode
);

// 获取文件状态
seastar::future<seastar::stat_data> file_stat(
const std::string& path,
seastar::follow_symlink follow
);
  1. DMA I/O:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// DMA 写入
seastar::future<size_t> dma_write(
uint64_t pos,
const void* buffer,
size_t len
);

// DMA 读取
seastar::future<size_t> dma_read(
uint64_t pos,
void* buffer,
size_t len
);

// 向量写入
seastar::future<size_t> dma_write(
uint64_t pos,
std::vector<iovec> iov
);
  1. ioctl 支持:
1
2
3
4
5
// 执行 ioctl
seastar::future<int> ioctl(
int request,
void* argp
);

Seastar 底层实现:

  1. Linux AIO:

    • 使用 io_submit() 提交异步 I/O
    • 使用 io_getevents() 获取完成事件
    • 通过 epoll 监听完成事件
  2. io_uring (如果支持):

    • 使用更高效的 io_uring 接口
    • 减少系统调用开销
    • 支持批量提交和完成
  3. 直接 I/O:

    • 绕过页缓存
    • 直接访问存储设备
    • 需要 O_DIRECT 标志

4.2 Linux 内核接口

4.2.1 ZBD 接口

头文件: <linux/blkzoned.h>

ioctl 命令:

  • BLKGETNRZONES: 获取 Zone 数量
  • BLKOPENZONE: 打开 Zone
  • BLKCLOSEZONE: 关闭 Zone
  • BLKFINISHZONE: 完成 Zone
  • BLKRESETZONE: 重置 Zone
  • BLKGETZONESZ: 获取 Zone 大小
  • BLKGETZONEINFO: 获取 Zone 信息

数据结构:

1
2
3
4
5
6
7
8
9
10
11
12
struct blk_zone {
__u64 start; // Zone 起始扇区
__u64 len; // Zone 长度(扇区)
__u64 wp; // 写指针(扇区)
__u8 type; // Zone 类型
__u8 cond; // Zone 状态
__u8 non_seq; // 非顺序写入标志
__u8 reset; // 重置推荐标志
__u8 resv[4];
__u64 capacity; // Zone 容量(扇区)
__u8 resv2[24];
};

4.2.2 NVMe 接口

头文件: <linux/nvme_ioctl.h>

ioctl 命令:

  • NVME_IOCTL_ID: 识别设备
  • NVME_IOCTL_ADMIN_CMD: 执行管理命令
  • NVME_IOCTL_IO_CMD: 执行 I/O 命令
  • NVME_IOCTL_SUBMIT_IO: 提交 I/O(已废弃)

关键数据结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct nvme_passthru_cmd {
__u8 opcode;
__u8 flags;
__u16 rsvd1;
__u32 nsid;
__u32 cdw2;
__u32 cdw3;
__u64 metadata;
__u64 addr;
__u32 metadata_len;
__u32 data_len;
__u32 cdw10;
__u32 cdw11;
__u32 cdw12;
__u32 cdw13;
__u32 cdw14;
__u32 cdw15;
__u32 timeout_ms;
__u32 result;
};

5. 设备选择与配置

5.1 配置参数

主设备类型:

1
seastore_main_device_type = "SSD" | "RANDOM_BLOCK_SSD"

设备路径:

1
2
3
seastore_device_path = "/dev/nvme0n1"  // NVMe 设备
| "/dev/sda" // 块设备
| "/dev/nvme0n1" // ZBD 设备

5.2 设备创建流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Device::make_device(device_path, device_type)

├─► 如果是 SEGMENTED 类型
│ │
│ ├─► device_type == SSD
│ │ │
│ │ ▼
│ │ BlockSegmentManager::create()
│ │
│ ├─► device_type == ZBD
│ │ │
│ │ ▼
│ │ ZBDSegmentManager::create()
│ │
│ └─► device_type == HDD
│ │
│ ▼
│ BlockSegmentManager::create()

└─► 如果是 RANDOM_BLOCK 类型


NVMeBlockDevice::create()

6. 性能特性

6.1 Seastar DMA 优势

  1. 零拷贝:

    • 数据直接从用户缓冲区传输到设备
    • 减少内核拷贝开销
  2. 异步 I/O:

    • 所有操作都是异步的
    • 不阻塞线程
    • 充分利用 CPU
  3. 批量操作:

    • 支持向量 I/O
    • 减少系统调用次数
  4. 事件驱动:

    • 基于 epoll/io_uring
    • 高效的事件处理

6.2 设备特定优化

6.2.1 ZBD 优化

  • 顺序写入: 利用 Zone 的顺序写入特性
  • Zone 管理: 批量重置 Zone,提高效率
  • 写指针跟踪: 自动管理写指针,避免覆盖

6.2.2 NVMe 优化

  • 多流写入: 根据数据生命周期选择流
  • 端到端保护: 利用硬件校验,减少软件开销
  • 原子写入: 利用 NVMe 原子写入单元
  • 写粒度对齐: 根据设备特性对齐写入

7. 与 SPDK 的关系

7.1 SeaStore 不使用 SPDK

重要说明: SeaStore 不直接使用 SPDK。它通过以下方式访问存储:

  1. Seastar DMA 接口: 主要 I/O 路径
  2. Linux 内核接口: ioctl 用于设备管理
  3. POSIX API: 文件操作

7.2 SPDK 在 Ceph 中的使用

SPDK 主要用于 AlienStore (BlueStore 的适配层):

1
2
3
4
5
6
7
8
Crimson OSD

├─► SeaStore (本文档)
│ └─► Seastar DMA

└─► AlienStore
└─► BlueStore
└─► SPDK BDEV

7.3 为什么 SeaStore 不使用 SPDK?

  1. Seastar 已提供高性能 I/O:

    • Seastar 的 DMA 接口已经足够高效
    • 支持 io_uring,性能接近 SPDK
  2. 简化架构:

    • 减少依赖
    • 降低复杂度
  3. 异步模型匹配:

    • Seastar 的异步模型与 SeaStore 完美匹配
    • 无需额外的适配层

8. 总结

8.1 支持的接口

设备类型 底层库 主要接口 特殊功能
SSD/HDD Seastar DMA dma_read/dma_write 批量 I/O
ZBD Seastar DMA + Linux ZBD dma_read/dma_write + ioctl Zone 管理
RANDOM_BLOCK_SSD Seastar DMA + Linux NVMe dma_read/dma_write + ioctl 多流、E2E 保护

8.2 核心库

  1. Seastar:

    • 异步 I/O 框架
    • DMA 接口
    • 事件驱动模型
  2. Linux 内核接口:

    • ZBD: linux/blkzoned.h
    • NVMe: linux/nvme_ioctl.h
  3. 系统调用:

    • AIO: io_submit/io_getevents
    • io_uring: (如果支持)
    • ioctl: 设备管理

8.3 设计特点

  1. 统一抽象: 通过 Device 接口统一不同设备类型
  2. 高性能: 基于 Seastar 的异步 I/O 和零拷贝
  3. 设备优化: 针对不同设备类型提供特定优化
  4. 简化架构: 不依赖 SPDK,减少复杂度

通过这种设计,SeaStore 能够高效地访问各种类型的存储设备,同时保持代码的清晰和可维护性。