Linux内核学习轨迹第七部:设备映射器Device Mapper与LVM实现(第七节)

DM设备映射 例1:创建20G物理卷pv创建物理卷pv   Free  pe 为0,因为目前没有吧物理卷加入卷组    创建卷组vg  运行命令可发现PE size 有数据了  调整PE大小   缩减vg (就是拿掉pv的过程,) 备注:发现 /dev/sdc1 不属于任何vg 擦除 /dev/sdc1 的数据   扩展vg容量   创建逻辑卷LV    格式化挂载LV   Mount    例二扩展逻辑卷... 阅读详情

7. 设备映射器Device Mapper与LVM实现

在Linux存储IO栈中,设备映射器(Device Mapper,简称DM) 是位于通用块层与块设备驱动之间的核心抽象层,也是企业级存储架构的基石。它的核心作用是对底层物理块设备(硬盘、SSD、RAID阵列等)进行虚拟化、逻辑组合与功能扩展,屏蔽底层硬件的差异,为上层提供统一的逻辑块设备接口。
逻辑卷管理(Logical Volume Manager,简称LVM)是Device Mapper最经典、最广泛的应用场景,它基于DM框架实现了磁盘空间的动态扩容、缩减、快照、条带化、镜像等高级功能,解决了传统分区无法动态调整大小、硬件利用率低、数据迁移困难等痛点,是生产环境中服务器存储配置的标准方案。
本章节基于Linux 6.6 LTS内核源码(DM核心代码位于drivers/md/dm/目录,LVM工具源码位于lvm2/项目),从底层到上层,完整拆解Device Mapper的核心架构、工作机制、核心组件,再深入LVM的实现原理、核心功能、操作流程与工程实践,同时补充生产环境中DM与LVM的调优技巧、故障排查方法与避坑指南,确保内容兼具源码深度与实践价值。

7.1 Device Mapper核心架构与设计思想

Device Mapper的核心设计思想是“分层虚拟化、模块化扩展”,它通过“映射表”将一个或多个底层物理块设备(称为“物理设备”),虚拟化为一个或多个上层逻辑块设备(称为“DM设备”)。上层应用(文件系统、用户态程序)只需与DM设备交互,无需关心底层物理设备的数量、类型、布局,由DM层自动完成逻辑地址到物理地址的映射,以及IO请求的转发、处理与优化。
7.1.1 DM在IO栈中的位置
衔接前序章节的IO全链路,DM层的定位如下:
通用块层(BIO/request) → 【Device Mapper层】 → 块设备驱动 → 物理存储硬件
  • 向上:DM层作为通用块层的“消费者”,接收通用块层下发的IO请求,将其转换为对底层物理设备的IO请求;
  • 向下:DM层作为块设备驱动的“调用者”,将处理后的IO请求下发给对应的物理设备驱动,完成最终的硬件读写;
  • 核心价值:在不修改上层应用、不改动块设备驱动的前提下,实现存储功能的灵活扩展(如加密、快照、条带化),以及物理设备的动态管理(如设备替换、容量扩容)。
7.1.2 DM的核心架构组件
Device Mapper的架构由4个核心组件组成,各组件职责清晰、解耦性强,构成了完整的虚拟化与扩展能力,具体如下:
1. DM驱动(DM Core)
DM驱动是Device Mapper的核心内核模块(dm_mod.ko),是整个DM层的“大脑”,负责管理所有DM设备、映射表、目标设备,以及IO请求的调度与转发。其核心职责包括:
  • 接收用户态工具(dmsetup)的命令,创建、删除、修改DM设备;
  • 解析映射表,维护逻辑地址与物理地址的映射关系;
  • 接收通用块层下发的IO请求,根据映射表将其转发到对应的目标设备(target)进行处理;
  • 管理目标设备(target)的加载、卸载与生命周期,支持模块化扩展新的target类型;
  • 处理IO请求的错误、超时,以及设备的热插拔、故障恢复。
2. DM设备(DM Device)
DM设备是DM层向上暴露的逻辑块设备,对应/dev/mapper/目录下的设备节点(如/dev/mapper/centos-root),与普通块设备(如/dev/sda1)完全兼容,上层应用可通过标准的块设备接口(open、read、write、ioctl)与其交互。
每个DM设备都有一个唯一的设备名(如centos-root)和UUID,用于标识设备;同时关联一个映射表,定义了该逻辑设备如何映射到底层物理设备。一个DM设备可以对应多个底层物理设备,也可以由多个目标设备(target)串联/并联组成,实现复杂的存储功能。
3. 映射表(Mapping Table)
映射表是DM设备的“配置文件”,也是DM层实现地址映射的核心,它定义了DM设备的逻辑地址空间如何映射到底层物理设备的物理地址空间,以及IO请求需要经过哪些target处理。
映射表的核心格式(用户态可通过dmsetup table命令查看)如下:
<逻辑起始扇区> <逻辑扇区数量> <target类型> <target参数> ...
  • 逻辑起始扇区:DM设备的逻辑地址起始位置(单位512字节扇区);
  • 逻辑扇区数量:该映射段对应的逻辑扇区长度,即该段逻辑地址的范围;
  • target类型:该映射段使用的目标设备类型(如linear、striped、snapshot);
  • target参数:针对该target的具体配置(如底层物理设备路径、物理起始扇区、条带大小等)。
一个DM设备的映射表可以包含多个映射段,每个映射段对应不同的target或不同的物理设备,DM层会根据IO请求的逻辑地址,找到对应的映射段,将IO请求转发给对应的target处理。
举个实际例子(LVM的root逻辑卷对应的DM映射表):
0 20971520 linear /dev/sda2 0

  • 逻辑起始扇区0,逻辑扇区数量20971520(对应10GB);
  • target类型为linear(线性映射);
  • target参数为/dev/sda2(底层物理设备)和0(物理起始扇区);
  • 含义:DM设备的0~20971520逻辑扇区,线性映射到底层/dev/sda2设备的0~20971520物理扇区。
4. 目标设备(Target)
Target是DM层的功能模块化组件,是实现IO请求处理、存储功能扩展的核心载体。每个target对应一种特定的存储功能(如线性映射、条带化、快照、加密),DM层将IO请求转发给target后,由target完成具体的IO处理(如地址转换、数据加密、快照生成),再将处理后的IO请求下发到底层物理设备。
Target的核心特性:
  • 模块化:每个target是一个独立的内核模块(如dm-linear.ko、dm-stripe.ko),可动态加载、卸载,支持灵活扩展;
  • 可组合:一个DM设备的映射表中可以包含多个target,IO请求会按映射表的顺序依次经过多个target处理,实现复杂功能(如“加密+快照”组合);
  • 透明性:target对上层应用和底层驱动完全透明,上层无需关心target的存在,底层驱动无需适配target的逻辑。
7.1.3 DM的核心数据结构拆解(Linux 6.6内核)
DM层的核心数据结构围绕“DM设备、映射表、target”展开,以下拆解驱动开发与原理理解必须掌握的核心结构体,剔除调试、统计类非核心字段。
1. DM设备结构体:struct dm_dev
struct dm_dev是DM层对底层物理设备的抽象,每个被DM使用的物理设备,都会对应一个dm_dev实例,用于管理物理设备的引用计数、设备信息、IO状态。核心定义(include/linux/dm.h):
struct dm_dev {
    // 物理设备的路径(如"/dev/sda2")
    char *name;
    // 物理设备的block_device实例指针
    struct block_device *bdev;
    // 引用计数,管理dm_dev的生命周期
    refcount_t refcnt;
    // 设备的打开计数,记录有多少DM映射段使用该设备
    int open_count;
    // 保护该结构体的自旋锁
    spinlock_t lock;
    // 设备的IO统计信息
    struct dm_io_stats stats;
    // 设备的私有数据,用于target存储自定义信息
    void *private;
};

核心要点:
  • DM层通过dm_dev结构体间接操作底层物理设备,不直接操作block_device,避免破坏底层设备的状态;
  • 引用计数refcnt确保物理设备在被DM使用期间,不会被意外卸载或删除;
  • open_count记录该物理设备被多少个DM映射段引用,只有当open_count为0时,才能释放该dm_dev实例。
2. Target结构体:struct dm_target
struct dm_target是DM层对目标设备的抽象,每个映射段对应一个dm_target实例,包含了target的类型、参数、处理函数等核心信息。核心定义:
struct dm_target {
    // target的名称(如"linear"、"striped")
    const char *name;
    // target的版本号
    unsigned int version[3];
    // IO请求处理函数,核心入口:接收DM层转发的IO请求,进行处理
    int (*map)(struct dm_target *ti, struct bio *bio);
    // IO完成回调函数,target处理完IO后触发
    void (*end_io)(struct dm_target *ti, struct bio *bio, blk_status_t status);
    // target初始化函数,创建target时调用
    int (*init)(struct dm_target *ti);
    // target清理函数,删除target时调用
    void (*exit)(struct dm_target *ti);
    // 处理ioctl命令,用于target的配置与查询
    int (*ioctl)(struct dm_target *ti, unsigned int cmd, unsigned long arg);
    // target的私有数据,存储target的配置参数(如物理设备、扇区偏移)
    void *private;
    // 关联的DM设备(dm_table)
    struct dm_table *table;
    // 该target对应的逻辑扇区范围(起始扇区、扇区数量)
    sector_t start;
    sector_t len;
};

核心要点:
  • map函数是target的核心入口,所有IO请求都会经过该函数处理,实现地址转换、功能扩展(如加密、快照);
  • private字段存储target的具体配置,不同类型的target,private的结构不同(如linear target存储物理设备和物理起始扇区,striped target存储条带大小、物理设备列表);
  • 每个target必须实现map函数,其他函数(end_io、init、exit)可根据需求选择性实现。
3. 映射表结构体:struct dm_table
struct dm_table是DM设备映射表的抽象,包含了该DM设备的所有映射段(dm_target实例)、逻辑扇区总大小、相关配置等信息,是DM层解析地址映射的核心。核心定义:
struct dm_table {
    // 该映射表对应的DM设备
    struct dm_device *dev;
    // 映射段(dm_target)链表,存储所有映射段
    struct list_head targets;
    // 映射表的逻辑扇区总大小(即DM设备的总容量)
    sector_t size;
    // 映射表的引用计数
    refcount_t refcnt;
    // 保护映射表的读写信号量
    struct rw_semaphore rwsem;
    // 映射表的标志位(如只读、可写、快照等)
    unsigned long flags;
    // 该映射表使用的所有底层物理设备(dm_dev链表)
    struct list_head devices;
    // 映射表的私有数据,用于存储全局配置
    void *private;
};

核心要点:
  • targets链表存储了该DM设备的所有映射段,DM层会遍历该链表,根据IO请求的逻辑地址找到对应的dm_target;
  • size字段定义了DM设备的总容量,即逻辑扇区的总数量,对应/dev/mapper/设备的大小;
  • devices链表存储了该映射表使用的所有底层物理设备,用于管理物理设备的引用与生命周期。
4. DM设备结构体:struct dm_device
struct dm_device是DM层对DM逻辑设备的抽象,对应/dev/mapper/下的一个设备节点,是DM层管理的核心对象。核心定义:
struct dm_device {
    // DM设备的名称(如"centos-root")
    char *name;
    // DM设备的UUID,唯一标识该设备
    char *uuid;
    // DM设备的主设备号和次设备号
    dev_t dev;
    // 该设备当前生效的映射表
    struct dm_table *table;
    // 该设备的gendisk实例,用于向通用块层注册块设备
    struct gendisk *disk;
    // 该设备的request_queue队列,用于接收通用块层的IO请求
    struct request_queue *queue;
    // 保护该设备的自旋锁
    spinlock_t lock;
    // 设备的状态标志(如激活、未激活、只读)
    unsigned int flags;
    // 设备的引用计数
    refcount_t refcnt;
    // 设备的私有数据,用于存储设备的全局配置
    void *private;
};

核心要点:
  • disk和queue字段是DM设备能被通用块层识别的关键,DM设备本质上是一个由DM层创建的虚拟块设备,通过gendisk向内核注册;
  • table字段指向当前生效的映射表,当映射表修改后(如扩容、快照),会替换该字段,实现DM设备的动态配置;
  • dev字段是DM设备的设备号,对应/dev/mapper/设备节点的设备号,与普通块设备一致。
7.1.4 DM的IO请求完整处理流程
DM层的核心工作是处理通用块层下发的IO请求,将其转换为对底层物理设备的IO请求,完整流程分为6个阶段,衔接前序章节的BIO/request生命周期:
1. 通用块层下发IO请求 → 2. DM设备接收IO请求 → 3. 映射表解析与target匹配 → 4. target处理IO请求 → 5. 下发IO到物理设备驱动 → 6. IO完成与回调

阶段1:通用块层下发IO请求
上层应用(文件系统、用户态程序)通过标准块设备接口(read、write)发起IO请求,通用块层将其封装为BIO/request请求,根据设备号找到对应的DM设备(struct dm_device),将IO请求下发到DM设备的request_queue队列。
阶段2:DM设备接收IO请求
DM设备的request_queue队列接收IO请求后,由DM驱动的队列处理函数(dm_request_fn)接收请求,进行初步校验(如IO请求的逻辑地址是否超出DM设备的容量),校验通过后,进入映射表解析阶段。
阶段3:映射表解析与target匹配
DM驱动遍历DM设备的映射表(struct dm_table)中的targets链表,根据IO请求的逻辑起始扇区,找到对应的映射段(struct dm_target):
  • 若IO请求的逻辑地址范围完全落在某个映射段内,直接匹配该映射段的target;
  • 若IO请求的逻辑地址范围跨多个映射段,DM驱动会将IO请求拆分为多个子请求,分别匹配对应的target;
  • 匹配完成后,将IO请求转发给对应的target处理。
阶段4:target处理IO请求
target的map函数接收IO请求,根据自身的功能逻辑处理请求,核心动作包括:
  • 地址转换:将IO请求的逻辑地址,转换为底层物理设备的物理地址(如linear target直接映射,striped target计算条带对应的物理地址);
  • 功能扩展:根据target类型执行额外处理(如snapshot target实现写时复制,dm-crypt target实现数据加密/解密);
  • 请求转发:将处理后的IO请求,转发到底层物理设备的block_device实例,进入块设备驱动层。
阶段5:下发IO到物理设备驱动
target处理完成后,IO请求被转发到底层物理设备的驱动,驱动将其转换为硬件指令,下发给物理存储设备,执行实际的读写操作(流程与第6章块设备驱动的IO处理一致)。
阶段6:IO完成与回调
物理设备执行完IO操作后,通过中断通知驱动,驱动完成IO收尾后,触发DM层的end_io回调函数,再由DM层的target执行end_io回调(如快照target更新写时复制状态,加密target完成解密收尾),最终将IO完成状态通知给通用块层,上层应用获取IO结果,完成整个IO闭环。

7.2 Device Mapper核心Target详解

Target是DM层功能扩展的核心,不同的target实现不同的存储功能,生产环境中最常用的target包括linear、striped、mirror、snapshot、crypt,以下逐一拆解其核心原理、实现逻辑、适用场景,结合Linux 6.6内核源码与生产实践。
7.2.1 linear(线性映射)Target
linear是最基础、最常用的DM target,核心功能是将多个底层物理设备按顺序串联起来,形成一个连续的逻辑地址空间,IO请求按逻辑地址顺序转发到对应的物理设备,实现“多盘合并”的效果。
核心原理
linear target的核心逻辑是“逻辑地址→物理地址”的线性映射,映射公式如下:
物理起始扇区 = 物理设备的起始扇区 + (逻辑扇区 - 该映射段的逻辑起始扇区)
  • 每个linear映射段对应一个底层物理设备,多个映射段按逻辑地址顺序排列,形成连续的逻辑空间;
  • 例如:两个物理设备/dev/sda2(10GB)和/dev/sdb1(20GB),通过linear target串联,DM设备的0~20971520逻辑扇区映射到/dev/sda2,20971521~62914560逻辑扇区映射到/dev/sdb1,最终DM设备总容量为30GB。
核心实现(源码片段)
linear target的核心实现位于drivers/md/dm-linear.c,核心结构体(struct linear_c)存储target的配置参数:
// linear target的私有数据结构体
struct linear_c {
    // 底层物理设备的dm_dev实例
    struct dm_dev *dev;
    // 物理设备的起始扇区(即该映射段对应的物理起始地址)
    sector_t start;
};

// linear target的IO处理函数(map函数)
static int linear_map(struct dm_target *ti, struct bio *bio)
{
    struct linear_c *lc = ti->private;
    sector_t sector = bio->bi_sector;

    // 计算IO请求在物理设备上的起始扇区
    bio->bi_sector = lc->start + (sector - ti->start);
    // 将IO请求的目标设备设置为底层物理设备
    bio->bi_bdev = lc->dev->bdev;

    // 转发IO请求到底层物理设备
    return DM_MAPIO_REMAPPED;
}

核心逻辑:
  • linear_map函数仅做简单的地址转换,将DM设备的逻辑扇区转换为底层物理设备的物理扇区,修改BIO的bi_bdev和bi_sector字段,然后将BIO转发到底层物理设备;
  • 无额外的IO处理开销,性能接近直接操作物理设备,是LVM、根文件系统分区的默认target。
适用场景
  • 多块物理设备合并为一个逻辑设备,提升存储容量;
  • 底层物理设备类型一致(如都是SSD),无需条带化、镜像等高级功能;
  • 生产环境中最广泛的场景:LVM的物理卷(PV)组合、根文件系统的逻辑卷(LV)。
7.2.2 striped(条带化)Target
striped target(简称条带target)的核心功能是将IO请求分散到多个底层物理设备上,实现IO并行处理,提升顺序读写吞吐量,适用于大数据、视频存储等顺序IO密集型场景。
核心原理
striped target的核心是“条带大小”(stripe size),即每次IO操作在单个物理设备上写入的数据量(默认64KB),其地址映射逻辑如下:
  1. 计算IO请求的逻辑扇区对应的“条带索引”:striped_index = (逻辑扇区 - 映射段起始扇区) / 条带扇区数;
  2. 计算对应的物理设备索引:dev_index = striped_index % 物理设备数量;
  3. 计算该物理设备上的物理起始扇区:物理扇区 = 物理设备起始扇区 + (striped_index / 物理设备数量) * 条带扇区数 + (逻辑扇区 % 条带扇区数)。
举个例子:3块物理设备,条带大小64KB(128个512字节扇区),IO请求的逻辑扇区为200:
  • 条带索引 = 200 / 128 = 1;
  • 设备索引 = 1 % 3 = 1(对应第2块物理设备);
  • 物理扇区 = 物理设备起始扇区 + (1 / 3) * 128 + (200 % 128) = 物理设备起始扇区 + 0 + 72 = 物理设备起始扇区 + 72。
核心优势:
  • 顺序IO请求会被拆分为多个子请求,并行下发到多块物理设备,吞吐量接近多块设备的总和;
  • 适用于大文件顺序读写场景,如HDFS、视频存储、日志服务。
核心实现(源码片段)
striped target的核心实现位于drivers/md/dm-stripe.c,核心结构体(struct stripe_c)存储target的配置参数:
// striped target的私有数据结构体
struct stripe_c {
    // 底层物理设备列表(dm_dev实例数组)
    struct dm_dev **devs;
    // 物理设备数量
    unsigned int num_devs;
    // 条带大小(单位:扇区数)
    sector_t stripe_size;
    // 条带大小的移位值(用于快速计算)
    unsigned int stripe_shift;
    // 每个物理设备的起始扇区
    sector_t *dev_starts;
};

// striped target的IO处理函数(map函数)
static int stripe_map(struct dm_target *ti, struct bio *bio)
{
    struct stripe_c *sc = ti->private;
    sector_t sector = bio->bi_sector - ti->start;
    sector_t stripe_index, offset_in_stripe, dev_offset;
    unsigned int dev_idx;

    // 计算条带索引、扇区在条带内的偏移量
    stripe_index = sector >> sc->stripe_shift;
    offset_in_stripe = sector & ((1 << sc->stripe_shift) - 1);
    // 计算对应的物理设备索引
    dev_idx = stripe_index % sc->num_devs;
    // 计算该物理设备上的偏移量
    dev_offset = (stripe_index / sc->num_devs) << sc->stripe_shift;
    dev_offset += offset_in_stripe;

    // 设置IO请求的目标物理设备和物理扇区
    bio->bi_bdev = sc->devs[dev_idx]->bdev;
    bio->bi_sector = sc->dev_starts[dev_idx] + dev_offset;

    return DM_MAPIO_REMAPPED;
}

核心逻辑:
  • 通过条带大小的移位运算,快速计算条带索引、设备索引和物理扇区,减少计算开销;
  • 对于跨条带的IO请求,DM驱动会将其拆分为多个子请求,分别下发到对应的物理设备,实现并行处理;
  • 条带大小的选择会影响性能:条带过大会导致单设备IO压力过大,条带过小会增加IO拆分开销,生产环境中通常设置为64KB~256KB。
适用场景
  • 顺序IO密集型场景:大数据、视频存储、日志服务、备份服务;
  • 多块物理设备(如SSD、RAID阵列),需要提升顺序读写吞吐量;
  • 注意:striped target不提供数据冗余,任何一块物理设备故障都会导致整个DM设备数据丢失,通常与RAID、mirror target配合使用。
7.2.3 mirror(镜像)Target
mirror target的核心功能是将IO请求同时写入多个底层物理设备,实现数据冗余与高可用,当其中一块物理设备故障时,可从其他镜像设备中恢复数据,适用于对数据安全性要求高的场景(如数据库、核心业务存储)。
核心原理
mirror target的核心是“双写机制”,即所有写IO请求会同时下发到所有镜像物理设备,只有所有设备都写入成功,才算IO完成;读IO请求可从任意一块镜像设备读取,实现负载均衡。其核心组件包括:
  1. 主镜像设备(primary device):默认的读IO来源设备;
  2. 从镜像设备(secondary device):用于数据冗余,与主设备保持数据一致;
  3. 日志设备(log device):用于记录未完成的IO请求,防止系统崩溃时数据不一致,支持“磁盘日志”和“内存日志”两种模式。
镜像同步机制:
  • 初始化阶段:将主镜像设备的数据同步到从镜像设备,确保初始数据一致;
  • 运行阶段:所有写IO同时写入主、从设备,日志设备记录IO状态;
  • 故障恢复阶段:当某一块镜像设备故障时,替换故障设备后,通过日志设备同步未完成的IO,恢复数据一致性。
核心实现要点
mirror target的核心实现位于drivers/md/dm-mirror.c,逻辑相对复杂,核心要点:
  1. 写IO处理:将写IO请求复制多份,分别下发到所有镜像设备,等待所有设备IO完成后,才返回IO成功;
  2. 读IO处理:默认从主镜像设备读取,可通过配置实现读负载均衡(轮流从不同镜像设备读取);
  3. 日志处理:使用环形日志记录未完成的IO请求,系统崩溃后,重启时通过日志恢复数据,避免数据不一致;
  4. 同步处理:提供“全量同步”(初始化时)和“增量同步”(故障恢复时)两种模式,减少同步开销。
适用场景
  • 对数据安全性要求高的场景:数据库、核心业务存储、金融交易数据;
  • 高可用架构:需要避免物理设备故障导致的数据丢失;
  • 注意:mirror target会增加写IO开销(写IO需要同时写入多块设备),读IO性能略有提升,适合读多写少的场景。
7.2.4 snapshot(快照)Target
snapshot target的核心功能是对DM设备创建“时间点快照”,即记录某个时间点的DM设备数据状态,后续对原设备的修改不会影响快照设备,快照设备可用于数据备份、故障恢复、测试验证等场景,是生产环境中数据保护的核心工具。
核心原理
snapshot target基于写时复制(Copy-On-Write,简称COW) 机制实现,核心逻辑如下:
  1. 快照创建时,不复制原设备的任何数据,仅创建一个“快照映射表”,记录原设备与快照设备的地址映射关系,此时原设备(源设备)和快照设备共享所有数据块;
  2. 当对源设备执行写IO时,DM层会先将该数据块的原始数据复制到“快照存储设备”(通常是一块独立的物理设备),然后再将新数据写入源设备;
  3. 对快照设备执行读IO时,若数据块未被修改(未触发COW),则直接从源设备读取;若数据块已被修改(触发COW),则从快照存储设备读取原始数据;
  4. 快照设备的容量由“快照存储设备”的大小决定,用于存储被修改的数据块,若快照存储设备满,快照会变为只读状态,无法继续记录修改。
核心优势:
  • 快照创建速度极快(无需复制数据),占用空间小(仅存储被修改的数据块);
  • 源设备与快照设备相互独立,源设备的修改不会影响快照,快照的读取也不会影响源设备;
  • 支持多快照:一个源设备可以创建多个快照,记录不同时间点的数据状态。
核心实现(源码片段)
snapshot target的核心实现位于drivers/md/dm-snapshot.c,核心结构体(struct snapshot_c)存储target的配置参数:
// snapshot target的私有数据结构体
struct snapshot_c {
    // 源设备(被快照的DM设备)的dm_dev实例
    struct dm_dev *origin;
    // 快照存储设备(用于存储COW数据块)的dm_dev实例
    struct dm_dev *cow;
    // 写时复制的块大小(默认4KB)
    unsigned int block_size;
    // 块大小的移位值(用于快速计算)
    unsigned int block_shift;
    // 快照的状态(激活、只读、满)
    unsigned int status;
    // 保护快照的自旋锁
    spinlock_t lock;
    // COW映射表,记录被修改的数据块的地址映射
    struct dm_cow_table *cow_table;
};

// snapshot target的写IO处理函数(map函数)
static int snapshot_map(struct dm_target *ti, struct bio *bio)
{
    struct snapshot_c *sc = ti->private;
    sector_t sector = bio->bi_sector - ti->start;
    unsigned int block = sector >> sc->block_shift;
    struct dm_cow_entry *entry;

    if (bio_data_dir(bio) == WRITE) {
        // 写IO:触发写时复制
        spin_lock(&sc->lock);
        // 检查该数据块是否已被修改(是否存在COW条目)
        entry = dm_cow_table_lookup(sc->cow_table, block);
        if (!entry) {
            // 未修改:复制原始数据到快照存储设备,创建COW条目
            if (dm_cow_copy_block(sc, block)) {
                spin_unlock(&sc->lock);
                return DM_MAPIO_ERROR;
            }
            entry = dm_cow_table_lookup(sc->cow_table, block);
        }
        spin_unlock(&sc->lock);

        // 将写IO转发到源设备(新数据写入源设备)
        bio->bi_bdev = sc->origin->bdev;
        bio->bi_sector = sc->origin->bdev->bd_start_sect + sector;
    } else {
        // 读IO:判断是否需要从快照存储设备读取
        spin_lock(&sc->lock);
        entry = dm_cow_table_lookup(sc->cow_table, block);
        spin_unlock(&sc->lock);

        if (entry) {
            // 已修改:从快照存储设备读取原始数据
            bio->bi_bdev = sc->cow->bdev;
            bio->bi_sector = sc->cow->bdev->bd_start_sect + entry->cow_block;
        } else {
            // 未修改:从源设备读取数据
            bio->bi_bdev = sc->origin->bdev;
            bio->bi_sector = sc->origin->bdev->bd_start_sect + sector;
        }
    }

    return DM_MAPIO_REMAPPED;
}

核心逻辑:
  • 写IO时,先检查数据块是否已触发COW,未触发则复制原始数据到快照存储设备,再写入新数据;
  • 读IO时,根据数据块是否被修改,选择从源设备或快照存储设备读取;
  • COW映射表记录被修改的数据块的地址,确保快照设备能正确读取原始数据;
  • 快照存储设备的容量决定了快照能记录的修改量,生产环境中需根据源设备的修改频率,合理设置快照存储设备的大小。
适用场景
  • 数据备份:创建快照后,将快照设备挂载,备份数据,不影响源设备的正常运行;
  • 故障恢复:当源设备数据损坏时,可通过快照设备恢复到之前的时间点;
  • 测试验证:基于快照设备进行测试,测试过程中对快照的修改不会影响源设备;
  • 注意:快照设备仅记录源设备的修改,若源设备故障(如磁盘损坏),快照设备也无法恢复数据,需与mirror target配合使用,实现高可用备份。
7.2.5 crypt(加密)Target
crypt target(简称dm-crypt)的核心功能是对DM设备的数据进行透明加密/解密,所有写入DM设备的数据会被自动加密,读取时会被自动解密,上层应用无需关心加密逻辑,实现数据的安全存储,适用于对数据保密性要求高的场景(如敏感数据存储、金融数据)。
核心原理
dm-crypt基于对称加密算法(如AES、DES)实现,核心逻辑如下:
  1. 初始化时,用户设置加密密钥(通过cryptsetup工具),密钥被安全存储在内核内存中,不会写入磁盘;
  2. 写IO时,DM层将IO请求的数据通过加密算法(如AES-256)加密,然后将加密后的数据写入底层物理设备;
  3. 读IO时,DM层从底层物理设备读取加密数据,通过相同的密钥和加密算法解密,然后将解密后的数据返回给上层应用;
  4. 加密粒度为“扇区”,每个扇区独立加密,使用不同的初始化向量(IV),避免相同明文加密后产生相同的密文,提升加密安全性。
核心实现要点
dm-crypt的核心实现位于drivers/md/dm-crypt.c,核心要点:
  1. 加密算法:支持AES、DES、3DES等多种对称加密算法,可通过用户态工具配置,默认使用AES-256;
  2. 密钥管理:密钥由用户提供,通过cryptsetup工具导入内核,内核使用密钥环(keyring)安全存储密钥,避免密钥泄露;
  3. 性能优化:使用内核加密API(Crypto API),支持硬件加速(如CPU的AES-NI指令集),减少加密/解密的性能开销;
  4. 透明性:上层应用无需修改任何代码,即可使用加密功能,DM层自动完成加密/解密,对上层完全透明。
适用场景
  • 敏感数据存储:金融数据、个人隐私数据、企业核心数据;
  • 合规要求:满足数据安全合规(如GDPR、等保),确保数据泄露后无法被破解;
  • 注意:密钥丢失会导致数据无法解密,生产环境中需做好密钥的备份与管理,避免密钥丢失。

7.3 LVM实现原理与核心组件

逻辑卷管理(LVM)是基于Device Mapper框架实现的逻辑存储管理工具,它将底层物理块设备(硬盘、SSD)抽象为物理卷(PV),将多个PV组合为卷组(VG),再从VG中划分出逻辑卷(LV),上层文件系统直接挂载LV,实现磁盘空间的动态管理、灵活扩展与高级功能(快照、条带化、镜像)。
LVM的核心价值的是打破传统分区的刚性限制:传统分区一旦创建,大小无法动态调整,且无法跨物理设备;而LVM的LV可以动态扩容、缩减,且可以跨多个PV(物理设备),极大提升了存储管理的灵活性和硬件利用率。
7.3.1 LVM的核心组件与层级关系
LVM的架构分为3个核心层级,从底层到上层依次为:物理卷(PV)→ 卷组(VG)→ 逻辑卷(LV),各层级相互关联、层层抽象,构成完整的LVM存储架构,具体关系如下:
底层物理设备(/dev/sda2、/dev/sdb1等) → 物理卷(PV) → 卷组(VG) → 逻辑卷(LV) → 文件系统 → 上层应用
1. 物理卷(Physical Volume,PV)
物理卷是LVM的底层基础,是对底层物理块设备(硬盘分区、整个硬盘、RAID阵列)的抽象,即将物理设备格式化为LVM格式,使其能被LVM识别和管理。
核心特性:
  • 物理卷可以是整个物理硬盘(如/dev/sda)、硬盘分区(如/dev/sda2)、RAID阵列(如/dev/md0),只要是块设备,均可格式化为PV;
  • 格式化PV时,会在物理设备的开头创建一个“PV头”(PV Header),记录PV的基本信息(如PV名称、UUID、大小、VG关联信息等);
  • PV被划分为多个“物理扩展块”(Physical Extent,PE),PE是LVM空间分配的最小单位,默认大小为4MB,可在创建PV时配置(通常设置为4MB、8MB、16MB);
  • 一个PV可以加入多个VG(但同一时间只能属于一个VG),加入VG后,其PE会被VG管理,用于分配给LV。
核心数据结构(LVM工具源码):
LVM的核心数据结构定义在lvm2/liblvm2.h,PV的核心结构体(struct physical_volume):
struct physical_volume {
    // PV的名称(如"/dev/sda2")
    char *name;
    // PV的UUID,唯一标识该PV
    char *uuid;
    // PV的总大小(单位:字节)
    uint64_t size;
    // PE的大小(单位:字节)
    uint64_t pe_size;
    // PV的总PE数量
    uint64_t pe_count;
    // 已分配的PE数量
    uint64_t pe_allocated;
    // 关联的VG(卷组)指针
    struct volume_group *vg;
    // PV的状态(激活、未激活、故障)
    enum pv_state state;
    // PV的元数据信息(存储在PV头中)
    struct pv_metadata metadata;
};

2. 卷组(Volume Group,VG)
卷组是LVM的核心管理层,是多个PV的集合,将多个PV的PE合并为一个“PE池”,统一管理和分配空间,为LV的创建提供空间支持。
核心特性:
  • 一个VG可以包含多个PV(最少1个,最多无限制,取决于系统配置),所有PV的PE会被合并为一个连续的PE池,LV从PE池中分配PE;
  • VG有一个“物理扩展块大小”(PE Size),所有加入该VG的PV,其PE大小必须与VG的PE大小一致,否则无法加入;
  • VG的总容量 = 所有PV的容量之和,VG的可用容量 = 总容量 - 已分配给LV的容量;
  • 可以动态向VG中添加PV(扩展VG容量),也可以从VG中移除未被使用的PV(缩减VG容量);
  • VG的元数据(Metadata)记录了VG的配置信息(如VG名称、UUID、PE大小、PV列表、LV列表等),元数据默认存储在每个PV的PV头中,实现冗余备份。
核心数据结构:
卷组的核心结构体(struct volume_group):
struct volume_group {
    // VG的名称(如"centos")
    char *name;
    // VG的UUID,唯一标识该VG
    char *uuid;
    // PE的大小(单位:字节)
    uint64_t pe_size;
    // VG的总PE数量(所有PV的PE之和)
    uint64_t total_pe;
    // VG的可用PE数量
    uint64_t free_pe;
    // PV列表(该VG包含的所有PV)
    struct list_head pv_list;
    // LV列表(该VG下的所有LV)
    struct list_head lv_list;
    // VG的状态(激活、未激活、只读)
    enum vg_state state;
    // VG的元数据信息(存储在PV头中)
    struct vg_metadata metadata;
    // 关联的DM设备(用于VG的虚拟化)
    struct dm_device *dm_dev;
};

3. 逻辑卷(Logical Volume,LV)
逻辑卷是LVM的上层逻辑设备,是从VG的PE池中分配一定数量的PE组成的,对应Device Mapper的DM设备,可被格式化、挂载,供上层应用使用,与传统分区完全兼容。
核心特性:
  • LV的大小 = 分配的PE数量 × PE大小,可动态调整(扩容:增加PE数量;缩减:减少PE数量);
  • LV对应DM设备,其底层由DM的target实现(默认linear,可配置为striped、mirror等);
  • 一个VG可以包含多个LV,每个LV独立使用一部分PE池,LV之间的空间相互独立;
  • LV支持高级功能:快照(创建LV的时间点快照)、条带化(跨多个PV实现IO并行)、镜像(实现数据冗余);
  • LV的名称通常为“VG名称-LV名称”(如centos-root),对应/dev/mapper/centos-root设备节点。
核心数据结构:
逻辑卷的核心结构体(struct logical_volume):
struct logical_volume {
    // LV的名称(如"root")
    char *name;
    // LV的UUID,唯一标识该LV
    char *uuid;
    // LV的大小(单位:字节)
    uint64_t size;
    // 分配给该LV的PE数量
    uint64_t pe_count;
    // 关联的VG(卷组)指针
    struct volume_group *vg;
    // LV的状态(激活、未激活、只读、快照)
    enum lv_state state;
    // LV对应的DM设备(dm_device实例)
    struct dm_device *dm_dev;
    // LV的DM映射表(由LVM自动生成)
    struct dm_table *dm_table;
    // LV的类型(线性、条带化、镜像、快照)
    enum lv_type type;
    // 快照相关信息(若为快照LV)
    struct lv_snapshot *snapshot;
};

7.3.2 LVM的核心工作机制
LVM的核心工作机制是“PE与LE的映射”,即逻辑扩展块(Logical Extent,LE)与物理扩展块(PE)的一一对应,LV的空间由多个LE组成,每个LE对应VG的PE池中一个PE,实现逻辑地址到物理地址的映射,最终通过Device Mapper的target完成IO请求的转发。
1. 核心概念:PE与LE
  • 物理扩展块(PE):PV的最小空间分配单位,也是VG的最小管理单位,默认4MB,所有PV的PE大小必须与所属VG的PE大小一致;
  • 逻辑扩展块(LE):LV的最小空间分配单位,LE的大小与所属VG的PE大小完全一致,一个LE对应一个PE;
  • 映射关系:LV的每个LE,都会映射到VG的PE池中一个未被分配的PE,LV的大小由LE的数量决定,扩容/缩减LV本质上就是增加/减少LE的数量,并调整LE与PE的映射关系。
2. LVM的IO请求流转机制
LVM的IO请求流转完全依赖Device Mapper的工作机制,具体流程如下:
  1. 上层应用(如文件系统)向LV(/dev/mapper/centos-root)发起IO请求,通用块层将其封装为BIO/request请求;
  2. 通用块层根据设备号找到对应的DM设备(LV对应的DM设备),将IO请求下发到DM层;
  3. DM层解析LV对应的映射表(由LVM自动生成),该映射表的target类型由LV的类型决定(默认linear);
  4. target(如linear)将IO请求的逻辑地址(LV的逻辑扇区)转换为底层PV的物理地址(PE对应的物理扇区);
  5. DM层将处理后的IO请求下发到底层PV对应的物理设备驱动,执行实际的读写操作;
  6. IO完成后,驱动通过DM层将结果返回给上层应用,完成IO闭环。
3. LVM元数据管理
LVM的元数据(Metadata)是LVM的“配置数据库”,记录了PV、VG、LV的所有配置信息,是LVM正常工作的核心,若元数据丢失或损坏,会导致LVM设备无法识别、数据丢失,因此元数据的备份与恢复至关重要。
元数据的存储位置:
  • 默认存储在每个PV的PV头中(位于PV的开头,占用少量空间),实现冗余备份,即使某一个PV故障,也可从其他PV中恢复元数据;
  • 可配置为“集中存储”(如存储在独立的磁盘分区),适用于大规模LVM部署场景。
元数据的核心内容:
  • PV信息:PV名称、UUID、大小、PE大小、关联的VG;
  • VG信息:VG名称、UUID、PE大小、总PE数量、可用PE数量、PV列表、LV列表;
  • LV信息:LV名称、UUID、大小、PE数量、LE与PE的映射关系、LV类型、关联的DM设备。
元数据的备份与恢复:
  • 自动备份:LVM每次修改配置(如创建PV、VG、LV,扩容/缩减LV)时,都会自动备份元数据到/etc/lvm/backup/目录,备份文件以VG名称命名;
  • 手动备份:可通过vgsave命令手动备份VG元数据;
  • 恢复:当元数据丢失或损坏时,可通过vgrestore命令,从备份文件中恢复元数据,恢复LVM的配置。

7.4 LVM高级功能实现与操作实践(续)

7.4.1 动态扩容与缩减(核心功能)

(续)动态扩容LV实现原理

1.lvextend命令的底层动作:

  • 首先检查VG的可用PE数量,确认满足扩容需求后,从VG的PE池中分配指定数量的PE,更新LV的pe_count(PE数量)和size(大小)字段;
  • 自动更新LV对应的DM映射表,新增对应的linear(或其他target)映射段,将新增的PE对应的物理地址,添加到DM设备的逻辑地址空间;
  • 通知DM层重新加载映射表,使扩容后的空间生效,此时LV的物理容量已完成扩展,但文件系统还未识别到新增空间。

2.文件系统扩展的核心逻辑:

  • resize2fs(ext4)、xfs_growfs(xfs)等命令,会扫描LV的新增空间,调整文件系统的inode、块位图、超级块等元数据,将新增空间纳入文件系统的管理范围;
  • 在线扩容时,文件系统会在不中断IO的情况下,完成元数据调整,确保数据安全,这也是LVM扩容的核心优势之一。
2. 动态缩减LV(需谨慎操作)
LV缩减的核心逻辑是:先收缩文件系统的大小,释放多余的空间,再从LV中移除对应的PE,将PE归还到VG的PE池,最后更新LV的大小与映射表。注意:缩减操作风险极高,若操作不当(如未收缩文件系统直接缩减LV),会导致数据丢失,建议操作前先备份数据。
前提条件:
  • 文件系统需支持在线缩减(ext4支持,xfs不支持xfs文件系统的LV缩减,需卸载后操作);
  • LV的实际使用空间需小于缩减后的目标大小,避免文件系统数据被截断;
  • LV需处于激活状态,但建议卸载文件系统后操作,降低数据丢失风险。
完整操作流程(以ext4文件系统为例):
# 1. 备份数据(关键步骤,避免操作失误导致数据丢失)
cp -r /mnt/data /backup/  # 假设LV挂载在/mnt/data

# 2. 卸载LV(建议操作,若ext4支持在线缩减,可跳过,但不推荐)
umount /mnt/data

# 3. 检查文件系统完整性(避免文件系统损坏导致缩减失败)
e2fsck -f /dev/mapper/centos-data  # ext4文件系统,-f强制检查

# 4. 收缩文件系统(将文件系统大小缩减到20GB,需小于LV当前大小)
resize2fs /dev/mapper/centos-data 20G

# 5. 缩减LV(将LV大小缩减到20GB,与文件系统大小一致)
lvreduce -L 20G /dev/mapper/centos-data
# 方式2:指定缩减的大小(-10G表示减少10GB)
# lvreduce -L -10G /dev/mapper/centos-data

# 6. 重新挂载LV,验证缩减结果
mount /dev/mapper/centos-data /mnt/data
df -h  # 查看文件系统大小
lvdisplay /dev/mapper/centos-data  # 查看LV大小
vgs  # 查看VG的可用PE数量(应增加)

实现原理:
  1. 文件系统收缩(resize2fs):先扫描文件系统的空闲空间,将文件系统的元数据(inode、块位图)调整到目标大小,释放多余的块空间,确保文件系统数据不会被截断;
  2. LV缩减(lvreduce):检查LV的实际使用空间,确认小于目标大小后,移除LV中多余的PE,将这些PE归还到VG的PE池,更新LV的pe_count和size字段;
  3. 更新DM映射表:删除LV对应的DM映射表中多余的映射段,通知DM层重新加载映射表,使缩减后的LV大小生效;
  4. 关键注意点:LV的缩减大小必须与文件系统的收缩大小一致,若LV缩减大小小于文件系统大小,会导致文件系统损坏,数据丢失。
7.4.2 LVM快照功能(数据备份与恢复)
LVM快照基于Device Mapper的snapshot target实现,核心是写时复制(COW)机制,分为快照LV(snapshot LV)和源LV(origin LV),快照LV记录源LV在创建快照时的时间点数据状态,后续源LV的修改不会影响快照LV,快照LV可用于数据备份、故障恢复、测试验证等场景。
1. 核心特性与分类
  • 按存储方式分类:
  1. 内部快照(Internal Snapshot):快照数据与源LV存储在同一个VG中,无需独立的PV,适用于临时快照、测试场景,优点是配置简单,缺点是会占用源LV所在VG的空间;
  2. 外部快照(External Snapshot):快照数据存储在独立的PV(快照存储设备)中,不占用源LV所在VG的空间,适用于长期快照、备份场景,优点是不影响源LV的空间使用,缺点是需要额外的存储设备。
  • 关键特性:
  1. 快照创建速度极快(无需复制源数据,仅创建映射表),占用空间小(仅存储源LV被修改的数据块);
  2. 快照LV可挂载、读写(默认只读,可配置为可写),挂载后可直接备份数据;
  3. 一个源LV可创建多个快照LV,记录不同时间点的数据状态;
  4. 若快照存储空间(COW空间)满,快照LV会变为只读状态,无法继续记录源LV的修改,需及时清理或扩容快照空间。
2. 实现原理(结合DM snapshot target)
LVM快照的底层完全依赖DM的snapshot target,LVM仅负责封装配置、管理元数据,核心原理与DM snapshot target一致,具体流程如下:
  1. 创建快照时,LVM会创建一个快照LV(对应DM的snapshot target),并指定源LV(origin LV)和快照存储设备(COW设备);
  2. LVM自动生成DM映射表,将快照LV的逻辑地址映射到源LV和快照存储设备,此时快照LV与源LV共享所有数据块,无额外空间占用;
  3. 当源LV执行写IO时,DM层触发COW机制:先将该数据块的原始数据复制到快照存储设备,再将新数据写入源LV;
  4. 当快照LV执行读IO时,若数据块未被修改(未触发COW),直接从源LV读取;若数据块已被修改(触发COW),从快照存储设备读取原始数据;
  5. 快照LV的大小由快照存储设备的大小决定,用于存储被修改的数据块,若快照存储设备满,快照LV变为只读,防止数据溢出。
3. 完整操作流程(生产环境常用)
# 1. 查看源LV信息(假设源LV为centos-data,挂载在/mnt/data)
lvdisplay /dev/mapper/centos-data

# 2. (可选)创建独立的PV和VG,用于存储快照数据(外部快照)
pvcreate /dev/sdd1  # 格式化物理设备为PV
vgcreate snapshot_vg /dev/sdd1  # 创建快照专用VG

# 3. 创建快照LV(外部快照,大小10GB,快照名称为data_snap,源LV为centos-data)
lvcreate -L 10G -s -n data_snap /dev/mapper/centos-data -V snapshot_vg
# 选项说明:
# -L 10G:快照LV大小(COW空间大小)
# -s:表示创建快照
# -n data_snap:快照LV名称
# -V snapshot_vg:指定快照存储的VG(外部快照),省略则为内部快照

# 4. 查看快照LV信息
lvdisplay /dev/mapper/centos-data_snap

# 5. 挂载快照LV,备份数据(快照默认只读,可通过-o ro挂载)
mkdir /mnt/snap_data
mount -o ro /dev/mapper/centos-data_snap /mnt/snap_data
cp -r /mnt/snap_data/* /backup/data_snap/  # 备份数据

# 6. (可选)测试快照恢复(假设源LV数据损坏,从快照恢复)
umount /mnt/data  # 卸载源LV
umount /mnt/snap_data  # 卸载快照LV
lvconvert --merge /dev/mapper/centos-data_snap  # 将快照合并到源LV(恢复数据)
mount /dev/mapper/centos-data /mnt/data  # 重新挂载源LV,验证数据

# 7. 删除快照LV(备份完成后,及时删除,释放空间)
umount /mnt/snap_data
lvremove /dev/mapper/centos-data_snap

4. 生产环境注意事项
  1. 快照COW空间大小配置:根据源LV的修改频率,合理设置快照大小,一般建议为源LV大小的10%~30%,若修改频率高(如数据库LV),需适当增大COW空间;
  2. 快照生命周期管理:快照仅用于短期备份,不建议长期保留,长期保留会导致COW空间满,且占用存储资源,备份完成后及时删除;
  3. 数据一致性:创建快照时,若源LV正在进行写IO,可能导致快照数据不一致,建议在业务低峰期创建快照,或先暂停业务再创建;
  4. 快照只读设置:生产环境中,快照LV建议设置为只读,防止误操作修改快照数据,影响备份有效性。
7.4.3 条带化LV(提升顺序IO吞吐量)
条带化LV基于Device Mapper的striped target实现,核心是将IO请求分散到多个PV(物理设备)上,实现IO并行处理,提升顺序读写吞吐量,适用于大数据、视频存储、日志服务等顺序IO密集型场景。
1. 核心特性与原理
  • 核心优势:顺序IO请求会被拆分为多个子请求,并行下发到多块物理设备,吞吐量接近多块设备的总和,大幅提升顺序读写性能;
  • 条带大小(Stripe Size):默认64KB,可在创建LV时配置(通常设置为64KB~256KB),条带大小决定了每次IO在单个PV上写入的数据量,影响IO性能;
  • 无数据冗余:条带化LV仅实现IO并行,不提供数据冗余,任何一块PV故障都会导致整个LV数据丢失,通常与RAID、mirror LV配合使用,实现高可用;
  • PE分配方式:条带化LV的PE会按条带大小,依次分配到多个PV上,形成条带分布,确保IO请求能分散到多块设备。
2. 实现原理(结合DM striped target)
  1. 创建条带化LV时,LVM会指定多个PV(至少2个),并设置条带大小,自动生成DM映射表,target类型为striped;
  2. DM的striped target根据条带大小,将LV的逻辑地址转换为多个PV的物理地址,实现IO请求的拆分与并行下发;
  3. 例如:3块PV,条带大小64KB,LV的逻辑地址0~63KB(128扇区)映射到PV1,64~127KB映射到PV2,128~191KB映射到PV3,192~255KB映射到PV1,以此类推,实现IO并行。
3. 完整操作流程
# 1. 准备多个PV(至少2个,假设为/dev/sda2、/dev/sdb1、/dev/sdc1)
pvcreate /dev/sda2 /dev/sdb1 /dev/sdc1

# 2. 创建VG(包含上述3个PV,PE大小设为64KB,与条带大小匹配)
vgcreate -s 64K striped_vg /dev/sda2 /dev/sdb1 /dev/sdc1

# 3. 创建条带化LV(大小30GB,条带大小64KB,使用3个PV)
lvcreate -L 30G -i 3 -I 64K -n striped_lv striped_vg
# 选项说明:
# -i 3:指定使用3个PV(条带数量)
# -I 64K:指定条带大小为64KB
# -n striped_lv:条带化LV名称

# 4. 格式化LV,挂载使用
mkfs.ext4 /dev/mapper/striped_vg-striped_lv
mkdir /mnt/striped_data
mount /dev/mapper/striped_vg-striped_lv /mnt/striped_data

# 5. 验证条带化效果(查看LV的条带配置)
lvdisplay /dev/mapper/striped_vg-striped_lv  # 查看Stripes和Stripe Size字段
dmsetup table striped_vg-striped_lv  # 查看DM映射表,确认target为striped

4. 生产环境最佳实践
  1. 条带大小选择:根据业务IO大小选择,大文件顺序读写(如视频、日志)建议设置为64KB~256KB,小文件随机读写建议设置为较小的条带大小(如32KB),减少IO拆分开销;
  2. PV选择:建议使用同型号、同容量、同性能的物理设备(如相同型号的SSD),避免因设备性能差异导致IO瓶颈;
  3. 高可用配合:条带化LV无数据冗余,生产环境中需配合RAID阵列(如RAID 0+1)或mirror LV,实现数据冗余与高可用;
  4. 性能监控:通过iostat、dstat等工具监控各PV的IO负载,确保IO请求均匀分布在多块设备上,避免单设备负载过高。
7.4.4 镜像LV(数据冗余与高可用)
镜像LV基于Device Mapper的mirror target实现,核心是将IO请求同时写入多个PV(镜像设备),实现数据冗余,当其中一块PV故障时,可从其他镜像设备中恢复数据,适用于对数据安全性要求高的场景(如数据库、核心业务存储)。
1. 核心组件与特性
  • 核心组件:
  1. 源LV(Primary LV):主镜像设备,默认的读IO来源设备;
  2. 镜像LV(Mirror LV):从镜像设备,与源LV保持数据一致,用于数据冗余;
  3. 日志设备(Log Device):用于记录未完成的IO请求,防止系统崩溃时数据不一致,支持“磁盘日志”(存储在独立PV)和“内存日志”(存储在内存),生产环境建议使用磁盘日志。
  • 核心特性:
  1. 双写机制:所有写IO请求会同时下发到源LV和镜像LV,只有所有镜像设备都写入成功,才算IO完成;
  2. 读负载均衡:读IO请求可从任意一块镜像设备读取,默认从源LV读取,可配置为轮流读取,提升读IO性能;
  3. 故障自动切换:当某一块镜像设备故障时,LVM会自动将IO请求切换到其他正常的镜像设备,不影响业务运行;
  4. 数据同步:初始化时,将源LV的数据同步到镜像LV;故障恢复时,替换故障设备后,通过日志设备同步未完成的IO,恢复数据一致性。
2. 实现原理(结合DM mirror target)
  1. 创建镜像LV时,LVM会指定源LV、镜像LV和日志设备,自动生成DM映射表,target类型为mirror;
  2. 写IO处理:DM的mirror target将写IO请求复制多份,分别下发到源LV和所有镜像LV,等待所有设备IO完成后,返回IO成功;
  3. 读IO处理:默认从源LV读取,可通过配置实现读负载均衡,轮流从不同镜像设备读取;
  4. 日志处理:使用环形日志记录未完成的IO请求,系统崩溃后,重启时通过日志恢复数据,避免数据不一致;
  5. 同步处理:初始化时执行全量同步,将源LV的数据复制到镜像LV;故障恢复时执行增量同步,仅同步未完成的IO和被修改的数据块,减少同步开销。
3. 完整操作流程
# 1. 准备多个PV(至少2个,用于源LV和镜像LV,假设为/dev/sda2、/dev/sdb1)
pvcreate /dev/sda2 /dev/sdb1 /dev/sdc1  # /dev/sdc1用于日志设备

# 2. 创建VG(包含上述3个PV)
vgcreate mirror_vg /dev/sda2 /dev/sdb1 /dev/sdc1

# 3. 创建镜像LV(大小20GB,1个镜像设备,日志设备为/dev/sdc1)
lvcreate -L 20G -m 1 -n mirror_lv mirror_vg /dev/sda2 /dev/sdb1 --logdevice /dev/sdc1
# 选项说明:
# -m 1:指定镜像设备数量(1个镜像,共2个设备:源+镜像)
# --logdevice /dev/sdc1:指定日志设备,避免使用内存日志
# 最后两个参数:源LV对应的PV和镜像LV对应的PV

# 4. 查看镜像LV信息(确认镜像状态、日志设备)
lvdisplay /dev/mapper/mirror_vg-mirror_lv

# 5. 格式化LV,挂载使用
mkfs.ext4 /dev/mapper/mirror_vg-mirror_lv
mkdir /mnt/mirror_data
mount /dev/mapper/mirror_vg-mirror_lv /mnt/mirror_data

# 6. 故障恢复测试(模拟镜像设备故障)
# 标记/dev/sdb1(镜像设备)为故障状态
pvchange -x n /dev/sdb1  # 禁止PV写入
lvdisplay /dev/mapper/mirror_vg-mirror_lv  # 查看镜像状态变为"Degraded"(降级)
# 替换故障设备(假设新设备为/dev/sdd1)
pvcreate /dev/sdd1
vgextend mirror_vg /dev/sdd1
lvconvert --replace /dev/sdb1 /dev/mapper/mirror_vg-mirror_lv  # 替换故障设备
lvdisplay /dev/mapper/mirror_vg-mirror_lv  # 查看镜像状态恢复为"Active"(激活)

# 7. 移除镜像(若无需冗余,可移除镜像,恢复为普通LV)
lvconvert -m 0 /dev/mapper/mirror_vg-mirror_lv

4. 生产环境注意事项
  1. 日志设备配置:生产环境建议使用独立的PV作为日志设备,避免使用内存日志(系统崩溃时日志丢失,导致数据不一致);
  2. 镜像设备选择:建议使用同型号、同容量、同性能的物理设备,确保镜像同步的效率和数据一致性;
  3. 性能影响:镜像LV会增加写IO开销(写IO需同时写入多块设备),读IO性能略有提升,适合读多写少的场景(如数据库读库);
  4. 故障监控:通过lvs、lvdisplay等命令监控镜像LV的状态,及时发现故障设备,避免多块设备同时故障导致数据丢失。

7.5 LVM元数据备份与恢复(生产环境必备)

LVM元数据是LVM的“配置数据库”,记录了PV、VG、LV的所有配置信息,包括PE与LE的映射关系、VG的PE池信息、LV的类型与大小等,若元数据丢失或损坏,会导致LVM设备无法识别、数据丢失,因此元数据的备份与恢复是生产环境中LVM管理的核心工作。
7.5.1 元数据的存储与自动备份

1.存储位置:

  • 默认存储在每个PV的PV头中(位于PV的开头,占用少量空间),实现冗余备份,即使某一个PV故障,也可从其他PV中恢复元数据;
  • 可通过lvmetad服务(LVM元数据守护进程),将元数据缓存到内存中,提升LVM操作的效率,适用于大规模LVM部署场景。

2.自动备份:

  • LVM每次修改配置(如创建PV、VG、LV,扩容/缩减LV,创建/删除快照)时,都会自动备份元数据到/etc/lvm/backup/目录,备份文件以VG名称命名(如centos);
  • 自动备份文件包含该VG的所有配置信息(PV、LV、PE映射等),保留历史版本,可用于恢复到之前的配置状态。
7.5.2 手动备份与恢复操作
1. 手动备份元数据
# 1. 备份指定VG的元数据(推荐方式)
vgsave centos -f /backup/lvm/centos_vg_backup  # 将centos VG的元数据备份到指定文件

# 2. 备份所有VG的元数据
vgsave --all -f /backup/lvm/all_vg_backup

# 3. 备份单个PV的元数据
pvsave /dev/sda2 -f /backup/lvm/sda2_pv_backup

2. 元数据恢复(分场景)
场景1:VG元数据丢失,但PV未损坏
# 1. 扫描系统中的所有PV(识别未被管理的PV)
pvscan  # 查看所有PV,确认丢失元数据的PV

# 2. 恢复VG元数据(从自动备份文件恢复)
vgrestore centos -f /etc/lvm/backup/centos  # 从默认备份文件恢复

# 3. 激活VG和LV
vgchange -ay centos  # 激活centos VG
lvchange -ay /dev/mapper/centos-root  # 激活指定LV

# 4. 验证恢复结果
vgs  # 查看VG是否正常
lvs  # 查看LV是否正常
mount /dev/mapper/centos-root /mnt  # 验证LV可正常挂载

场景2:PV损坏,元数据丢失(需从备份恢复)
# 1. 替换损坏的PV(假设损坏的PV为/dev/sda2,新PV为/dev/sdd1)
pvcreate /dev/sdd1

# 2. 恢复VG元数据(从手动备份文件恢复)
vgrestore centos -f /backup/lvm/centos_vg_backup

# 3. 将新PV替换损坏的PV
vgextend centos /dev/sdd1  # 将新PV添加到VG
vgreduce centos /dev/sda2  # 移除损坏的PV

# 4. 激活VG和LV,验证数据
vgchange -ay centos
lvdisplay /dev/mapper/centos-root

场景3:元数据损坏,无法识别VG(紧急恢复)
# 1. 强制扫描所有PV,收集元数据碎片
pvscan --cache --force

# 2. 尝试恢复VG元数据(从最近的自动备份)
vgcfgrestore centos -f /etc/lvm/backup/centos

# 3. 若恢复失败,使用元数据修复工具
lvmdiskscan  # 扫描所有LVM相关设备
vgcfgrestore --force centos  # 强制恢复

# 4. 激活VG和LV,检查数据完整性
vgchange -ay centos
e2fsck -f /dev/mapper/centos-root  # 检查文件系统完整性

7.5.3 生产环境元数据管理最佳实践
  1. 定期手动备份:每周/每月手动备份一次元数据,存储到独立的备份服务器,避免与源数据存储在同一设备;
  2. 保留自动备份:不要删除/etc/lvm/backup/目录下的自动备份文件,保留至少3个月的历史版本;
  3. 元数据监控:通过lvs、vgs、pvs等命令定期检查LVM配置,发现元数据异常及时处理;
  4. 应急方案:提前制定元数据丢失的应急恢复流程,明确备份文件的存储位置、恢复步骤,避免故障时手忙脚乱。

7.6 生产环境LVM与DM避坑指南

7.6.1 核心避坑要点(高频错误)

1.LV扩容/缩减的致命错误:

  • 错误1:未扩展文件系统直接扩容LV,导致新增空间无法被识别,虽不影响现有数据,但空间浪费;
  • 错误2:未收缩文件系统直接缩减LV,导致文件系统损坏,数据丢失(最常见、最严重的错误);
  • 错误3:LV缩减大小小于文件系统实际使用大小,导致文件系统截断,数据丢失。

2.快照功能的常见问题:

  • 问题1:快照COW空间满,导致快照变为只读,无法继续记录源LV的修改,需及时删除快照或扩容COW空间;
  • 问题2:创建快照时,源LV正在进行大量写IO,导致快照数据不一致,备份无效;
  • 问题3:长期保留快照,占用大量存储资源,且增加源LV的写IO开销(每次写都需触发COW)。

3.DM与LVM的兼容性问题:

  • 错误1:手动修改DM映射表,导致LVM配置与DM映射表不一致,LVM操作失败;
  • 错误2:使用废弃的DM target(如旧版本的raid1 target),导致驱动加载失败,DM设备无法激活;
  • 错误3:内核版本升级后,未更新LVM工具,导致LVM操作与内核DM层不兼容,出现异常。

4.物理设备管理错误:

  • 错误1:未卸载LV、未停用VG,直接移除物理设备,导致元数据损坏、数据丢失;
  • 错误2:将不同PE大小的PV加入同一个VG,导致PV添加失败;
  • 错误3:镜像LV的镜像设备与源设备性能差异过大,导致镜像同步卡顿,IO延迟升高。
7.6.2 生产环境最佳实践

1.规划先行:

  • PV规划:根据业务需求,合理划分物理设备为PV,避免将不同用途、不同性能的物理设备混为一个PV;
  • VG规划:按业务类型划分VG(如数据库VG、日志VG、备份VG),避免单个VG过大,便于管理和故障隔离;
  • PE大小规划:根据LV的大小和用途选择PE大小,小LV(如小于10GB)建议使用4MB PE,大LV(如大于100GB)建议使用16MB~64MB PE,提升空间分配效率。

2.操作规范:

  • 所有LVM操作前,必须备份数据和元数据,尤其是缩减、删除、替换设备等高危操作;
  • 扩容/缩减LV时,严格遵循“先文件系统、后LV”的顺序(扩容:LV→文件系统;缩减:文件系统→LV);
  • 避免在业务高峰期执行LVM高危操作(如扩容、缩减、快照创建),减少对业务的影响。

3.监控与运维:

  • 定期监控LVM状态:使用lvs、vgs、pvs查看PV、VG、LV的状态,关注可用空间、镜像状态、快照状态;
  • 监控IO性能:使用iostat、dstat、iotop监控LV、PV的IO负载,及时发现IO瓶颈;
  • 定期检查文件系统完整性:使用e2fsck(ext4)、xfs_check(xfs)检查文件系统,避免文件系统损坏。

4.高可用设计:

  • 关键业务的LV,建议使用镜像LV或配合RAID阵列,实现数据冗余;
  • 快照用于短期备份,长期备份建议使用专业的备份工具(如rsync、backupninja),将数据备份到独立的备份服务器;
  • 配置LV的监控告警,当VG可用空间不足、快照COW空间满、镜像状态异常时,及时触发告警。
7.6.3 故障排查方法

1.LV无法激活:

  • 排查步骤:使用lvdisplay查看LV状态,使用dmesg查看内核日志,检查PV是否正常、元数据是否损坏、DM驱动是否加载;
  • 解决方法:修复损坏的PV、恢复元数据、加载DM驱动,若LV状态为“inactive”,使用lvchange -ay激活。

2.元数据损坏:

  • 排查步骤:使用pvscan、vgscan扫描PV、VG,查看元数据是否完整,检查备份文件是否存在;
  • 解决方法:从自动备份或手动备份中恢复元数据,若备份丢失,使用vgcfgrestore --force强制恢复。

3.IO异常(延迟高、吞吐量低):

  • 排查步骤:使用iostat查看PV、LV的IO负载,使用dmsetup status查看DM设备状态,检查target配置是否合理;
  • 解决方法:调整条带大小、优化镜像同步策略、替换性能较差的物理设备,若为快照导致的IO延迟,及时删除快照。

4.快照COW空间满:

  • 排查步骤:使用lvdisplay查看快照LV的空间使用情况,使用df -h查看快照存储设备的空间;
  • 解决方法:删除无用的快照、扩容快照COW空间,或清理源LV的无用数据,减少修改量。
【基础演练】Playwright 极简复习:作为 AI-RPA 底座的 Playwright 核心用法 摘要: 2026年,Playwright已从测试工具升级为AI-RPA标准执行底座,周下载量超3300万,被browser-use等AI Agent项目广泛采用。其核心架构通过Playwright Server实现多浏览器协议适配,支持远程连接与高并发隔离(BrowserContext)。自动等待机制显著提升稳定性,但高频交互场景可能需直接使用CDP协议。Playwright凭借跨浏览器兼容性、轻量级隔离和微软官方支持,成为测试、RPA和AI Agent领域的事实标准,未来将主导对话式自动化市场。 阅读详情

相关推荐

李宏毅机器学习hw3

Homework 3 - Convolutional Neural Network 1作业概述 在food_11文件中有三个文件分别是training、validation、testing,打开training和validation可以看见总共有11种食物(0-10的类别编号),而testing是没有类别编号的。 然后用卷积神经网络对食物图片进行分类。 2导入用到的库 torch库 这个我弄了半天,直接pip总是timeout,用镜像源下下来又出现OSError: [WinError..

老问题的博客 4189

UEFI Shell编程和使用

参考 shell - 在 UEFI shell 映射中,FS0和BLK0之間的區別是什麼? UEFI Shell 进入UEFI Shell 在UEFI的Front Page,选择Boot Manager,进入Shell,输入help命令可以得到命令列表。 mapping table Mapping table FS0: Alias(s):HD21a0e0b:;BLK1: PciRoot(0...

黑客三遍猪 8765

DeepSeek的训练与优化流程

DeepSeek的训练体系不仅追求极致的计算效率,更注重将物理规律、人类知识、安全伦理深度融入训练过程,其"效率-安全-可解释性"三角平衡的设计理念,为行业提供了重要参考范式。

m0_56896669的博客 1437

【磁盘】Linux dm-0 dm-1 dm-2 设备映射

在Linux系统中你使用一些命令时(如nmon、iostat ),有可能会看到一些名字为dm-xx的设备,那么这些设备到底是什么设备呢,跟磁盘有什么关系呢? 其实dm是Device Mapper的缩写,Device Mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机制下,用户可以很方便的根据自己的需要制定实现存储资源的管理策略,当前比较流行的 Linux 下的逻辑卷管理器如 LVM2(Linux Volume Manager 2 version)、EVMS(E

michaelwoshi的博客 1万+

NVIDIA vGPU三种授权方式(个人玩家版)

NVIDIA vGPU三种方式搭建授权服务器(个人玩家版)

wstc2689784536的博客 8439

YOLOv11模型剪枝实战:让检测速度翻倍,精度仅降1%的终极优化教程

模型剪枝是YOLOv11落地边缘场景的“关键一步”——它让模型在“瘦身”的同时,几乎不损失精度。从稀疏化训练到通道剪枝,再到工程部署,本教程已为你打通全流程。现在就动手尝试,让你的YOLOv11模型在嵌入式设备、移动端上也能“飞起来”——相信我,当你看到剪枝后的模型在资源受限设备上仍能精准检测时,你会明白这份技术的价值。

m0_64661770的博客 711

linux dm-0 dm-1 设备映射 简介

在linux系统中你使用一些命令时(例如nmon、iostat 如下截图所示),有可能会看到一些名字为dm-xx的设备,那么这些设备到底是什么设备呢,跟磁盘有什么关系呢?以前不了解的时候,我也很纳闷. 其实dm是Device Mapper的缩写,Device Mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机制下,用户可以很方便的根据自己的需要制定实现存储资源的管理策略,当前比较流行的 Linux 下的逻辑卷管理器如 LVM2(Linux Volume Mana

whatday的专栏 4万+

车辆网络安全ISO/SAE 21434解读(一)概述

ISO/SAE 21434 《道路车辆 网络安全工程》是SAE和ISO共同制定的一项针对道路车辆的网络安全标准,它是一个面向汽车行业全供应链(OEM及各级供应商)的车辆网络安全管理指导文件

xiaoxiabang的博客 8426

OS操作系统Linux - DM 设备 (Device Mapper)

Device Mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制。 Device mapper本质功能就是根据映射关系和target driver描述的IO处理规则,将IO请求从逻辑设备mapped device转发相应的target device上。 Device mapper在内核中作为一个块设备驱动被注册的,它包含三个重要的对象概念,mapped device、映射表、target device。 ...

迟来大师的博客 1556

【推荐100个unity插件之2】 DoTween动画插件的安装和使用整合(最全)

摘要:DOTween动画插件使用指南 DOTween是Unity中强大的动画插件,比原生动画更便捷。安装后需进行初始化设置,通过工具菜单生成程序集。主要提供通用方法(DOTween.To)和便捷方法(如transform.DOMove)两种动画实现方式。插件支持丰富的动画设置(循环、缓动等)和事件回调(OnComplete、OnKill)。还提供动画管理功能,包括清除(DOTween.Clear)、完成(DOTween.CompleteAll)和终止(DOTween.KillAll)动画。常用功能涵盖移动、

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 1万+

matlab SAR图像的多视滤波

单视复数数据(Single Look Comple)是原始的最高分辨率数据,但是从单个像元散射的雷达回波信号的相干叠加,导致强度信息有很多噪声。SAR的多视处理是对SLC数据方位向和/或距离向做平均,得到的结果是多视后的强度数据。多视处理可以提高SAR图像的信噪比,有效抑制斑点噪声,但是会降低方位分辨率。

点云侠的博客 534

MAPPING TABLE

Oracle supports bitmap indexes on partitioned and nonpartitioned index-organized tables.A mapping table is required for creating bitmap indexes on an index-organized table. The mapping table

Chris的Oracle学习笔记 4958

uniapp [全端兼容] - 详细实现下拉筛选菜单功能组件,下拉式筛选菜单效果,类似电商美团点击筛选项底部弹出列表点击选择后自动收起(详细示例代码,全端兼容小程序、安卓苹果app、h5网页、nvue

uni-app,vue2,vue3,向下弹出菜单包含筛选功能,日期筛选,下拉框选择器,下拉多条件筛选,下拉式筛选菜单,uniApp如何实现条件下拉筛选,Uniapp官方条件筛选组件,下拉框式条件筛选插件组件,最好用的插件组件,多端兼容,下拉菜单筛选,过滤筛选列表,下拉筛选栏,多列联动的条件筛选,下拉菜单地区和筛选,微信小程序,支付宝小程序,h5网页,移动端网站,安卓app,苹果app,nvue,uniapp所有平台兼容,下拉菜单筛选,下拉式条件筛选,下拉列表,点击排序,级联筛选,uniapp实现下拉筛选

🏆 前端领域优质创作者 1万+

mapping table的作用

Oracle supports bitmap indexes on partitioned and nonpartitioned index-organized tables. A mapping table is requir...

cuiqiu7870的博客 836

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Linux中的Device Mapper

在linux系统中你使用一些命令时(例如nmon、iostat 如下截图所示),有可能会看到一些名字为dm-xx的设备,那么这些设备到底是什么设备呢,跟磁盘有什么关系呢?以前不了解的时候,我也很纳闷. 其实dm是Device Mapper的缩写,Device Mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机制下,用户可以很方便的根据自己的需要制定实现存储资源的管理策略,当前比较流行的 Linux 下的逻辑卷管理器如 LVM2(Linux Volume Mana

温其如玉的博客 1173

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

linux 添加磁盘映射dm,Linux上的设备映射

Linux上的设备映射介绍本文的创作灵感来自于EMC中文技术社区的一个真实的客户答疑,主题是关于“Linux中安装了powerpath之后为什么还会有dm设备”。这些问题应该已经解决了,作者特此将其整理成文,以飨读者。更多信息问题:在RHEL6.3中安装了powerpath5.7之后,OS上已经正确生成了emcpower磁盘设备(使用CX4-240存储),并配置到LVM中使用。但是今天发现,使用i...

weixin_42495661的博客 1938

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

MAPPING TABLE 的作用

MAPPING TABLE 的作用   Oracle supports bitmap indexes on partitioned and nonpartitioned index-organized tables.A mapping table is required f

7009

索引组织表中的MAPPING TABLE

Oracle supports bitmap indexes on partitioned and nonpartitioned index-organized tables.A mapping table is required for creating bitmap indexes on an index-organized table. The mapping table is a h

甲骨文可乐的专栏 1103

查看dm-0设备

root@localhost:~# lvdisplay|awk '/LV Name/{n=$3} /Block device/{d=$3; sub(".*:","dm-",d); print d,n;}' dm-0 /dev/datavg/datalv dm-1 /dev/ubuntu108-vg/root dm-2 /dev/ubuntu108-vg/swap_1

天空之蓝的博客 7734

linux LVM分区查看dm设备

linux LVM分区查看dm设备 在linux中iostat -d查看磁盘状态时,有的会有如下dm-0,dm-1的条目.  Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn  sda               0.19         0.57         2.92     341

mtm2012的专栏 5185

linux dm是什么设备,这是dm-0装置是什么?

您可以使用sardf -h# df -hFilesystem Size Used Avail Use% Mounted on/dev/mapper/VolGroup00-rootvol24G 4.1G 18G 19% /tmpfs 7.8G 0 7.8G 0% /dev/shm/dev/mapper/VolGroup01...

weixin_34493871的博客 2107

Linux 创建RAID设备 LVM设备

<br />Soft Raid<br /> fdisk /dev/sda<br />  p n t 6 fd<br /> <br /> ls /dev<br /> <br /> cat /proc/mdstat<br /> mdadm -C /dev/md0 -a yes -l 0 -n 2 /dev/sda6 /dev/sda7<br />  -a 当启用设备的时候,自动生成文件<br />  -a<br />   给已经创建好的RIAD添加热佩芬盘<br />   mdadm -a /dev/md0 /

325

ssd磁盘存储原理及产生io抖动原因

作为一名存储工程师怎么可以不了解存储的物理介质呢,与磁盘相比,SSD具有很高的IO性能,但是和磁盘相比,SSD的性能稳定性不如磁盘好。在使用过程中,SSD的性能会发生变化,而不能保持很强的一致性。这是SSD的一个特性,性能会随着时间、使用寿命发生变化。对于存储系统设计而言,这种不确定的性能变化以及抖动都会对系统整体性能造成影响。因此,对于闪存存储系统设计者而言,需要了解SSD内部的工作机制以及熟悉导致IO抖动的因素。在此之前先来了解一下ssd磁盘的基础原理。 存储原理 SSD主要由SSD控制器,Flas

cyq6239075的博客 4451

浅谈 Android 的安全启动和完整性保护

在 IoT 中保证设备安全性的重要一环就是保证代码的完整性,不让恶意代码影响业务的正常逻辑。一般而言是及时修复现有攻击面所面临的漏洞,比如浏览器、蓝牙、调试接口;另一方面需要确保的是即便恶意代码获取了执行权限,也无法修改系统镜像进行持久化。针对这点所构造的安全方案通常称为 Secure Boot,对于不同的厂商,实现上可能会引入不同的名字,比如 Verified Boot、High Assurance Boot 等等,但本质上都是类似的。 Secure Boot Secure Boot 顾名思义就是安全启

有价值炮灰 5410

linux DM设备

Device Mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机制下,用户可以很方便的根据自己的需要制定实现存储资源的管理策略,当前比较流行的 Linux 下的逻辑卷管理器如 LVM2(Linux Volume Manager 2 version)、EVMS(Enterprise Volume Management System)、dmraid(Dev

C_C518888888888的专栏 2728

加载模块时出现unknown partition table 的原因

块设备驱动程序通过调用add_disk函数来向系统注册一个磁盘设备。当驱动程序试图这么做的时候,内核试图读取该磁盘设备上的分区信息,对每个有效分区形成一个驱动模型中设备device对象,并通过device_add添加到系统中,但此时这些分区不会产生对应的block_device对象,直到分区设备被打开。 如果此时加入的设备尚未产生有效分区,所以在add_disk时系统将无法获得分区信息,因此产生u

u011673554的博客 9907
上一篇: Linux内核学习轨迹第七部:块设备驱动框架与开发流程(第六节)
下一篇: 显示屏接口对比:DPI、DBI、DSI详解
WythePlus
博客等级 码龄11年 397粉丝 300原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值