LVM项目小结

1 篇文章 0 订阅
1 篇文章 0 订阅

无用的瞎bb:

  距离上一篇博客其实已经过去很久了,本身没有懈怠但是鉴于实在太忙便一直没更新。
  花了很长时间对公司产品做windows动态磁盘(ldm)和lvm的支持。也就是提供一个ldm或者lvm的镜像使用dokany对其进行虚拟重组,方便lvm相关信息的展示以及其lv分区中文件系统的进一步解析。
  因为ldm是基于lvm的衍生品所以下面主要会讲关于lvm的故事,原理都是相通的。

0x01:lvm是啥?

  lvm( Logical Volume Manager)是linux内核提供的逻辑卷管理工具。核心原理就是在硬盘和分区表之间增加一个抽象层(逻辑卷)。这个抽象层用于管理底层真实硬盘对上则表现为一个超大的逻辑卷。基础这种思想而扩展出来的能力就很多了。比如用一块硬盘创建出raid整列(RAID LOGICAL VOLUMES),动态伸缩逻辑分区,逻辑卷加密等等。当使用lvm时,有一个致命的缺点需要特别注意的:windows系统不支持lvm,所以任何形式的lvm分区在windows系统中是不识别的,与之相对应的,windows会使用LDM(Logical Disk Manager )管理多磁盘构成逻辑卷。
  lvm的优点总结如下:
  1. 将任意多个物理磁盘(或者分区)组合成一个大的逻辑磁盘。
  2. 更好利用碎片部分,lv可能是由多个硬盘的片段构成
  3. 允许创建具有raid功能的lv分区
  4. 可以动态的创建,删除,伸缩lv卷的大小
  

0x02: lvm与lvm2

  lvm目前有2个版本:lvm和lvm2。在目前大部分的linux发行版都是支持lvm的,而lvm2则需要linux kernel 2.6.9(事实上也已经被大部分主流发行版支持,所以我们现在日常所说的lvm多数都指lvm2,当然本篇文章也是面向lvm2的),详情见下表(大于等于以上版本均可):

linux发行版linux内核版本号
Debian 4.02.6.18
Mint 2.02.6.17
Ubuntu 5.042.6.10
Fedora 3.02.6.9
Opensuse 9.32.6.11.4
Arch 0.72.6.10
Centos 4.82.6.9

我们可以通过 sudo lvm version 命令检查当前系统中lvm版本:
这里写图片描述

0x03:PV,VG,LV,PE

  在理解lvm之前一定要理解一些基础概念,并懂得它们的继承层级关系:pv,vg,lv和pe.
  PV(physical volume):物理卷在逻辑卷管理系统最底层,可为整个物理硬盘或实际物理硬盘(/dev/sdb)上的某个分区(/dev/sdb1)。在制作lvm的第一步就是将磁盘(分区)使用pvcreate命令变为PV分区,pvremove命令将PV转换回普通分区,表示lvm不再使用这部分空间。需要注意的是一个磁盘转变为PV后,并不是这个磁盘所有的空间都能使用。在描述PV的结构体(PhysicalVolume)中有个字段叫PEStart,它的单位是扇区。表示从硬盘或者分区起始的第PEStart个扇区是真正被使用的。
  VG(volume group):卷组建立在物理卷上,一卷组中至少要包括一物理卷,卷组建立后可动态的添加卷到卷组中,一个逻辑卷管理系统工程中可有多个卷组。通过vgcreate创建VG,使用vgextend和vgremove控制物理卷的添加和删除。
  LV(logical volume):逻辑卷建立在卷组基础上,卷组中未分配空间可用于建立新的逻辑卷,逻辑卷建立后可以动态扩展和缩小空间。
  PE(physical extent):物理区域是物理卷中可用于分配的最小存储单元,物理区域大小在建立卷组时指定,一旦确定不能更改,同一卷组所有物理卷的物理区域大小需一致,新的pv加入到vg后,pe的大小自动更改为vg中定义的pe大小。注意PE的单位是扇区数。假设某个VG占用100个PE空间,每个PE大小是4096,每个扇区大小是512字节。则这个VG占用字节大小是 100*4096*512字节。

0x04: 核心数据结构

  从上面的分层也可以看出来,lvm的数据结构主要有三个:PV,VG,LV.它们以文本(human readable)配置文件的形式存储在扇区上,关于更多的信息请仔细阅读一下libvslvm库的libvslvm_physical_volume_read_label函数。
  该数据结构参考自libyal并略有修改:https://github.com/libyal/libvslvm

struct libvslvm_physical_volume_header
{
    uint8_t identifier[39]; // 当前pv的uuid(vg允许有多个pv设备)
    uint64_t volume_size;
};

typedef struct logical_volume_strip logical_volume_strip_t;
struct logical_volume_strip
{
    char pv_name[128];  // "pv0"
    uint64_t pe_start;
};

typedef struct logical_volume_segment logical_volume_segment_t;
struct logical_volume_segment
{
    uint64_t start_extent;
    uint64_t extent_count;
    char type[16];
    uint8_t stripe_count;
    logical_volume_strip_t** stripes; // stripe_count个 strip
};

typedef struct logical_volume logical_volume_t;
struct logical_volume
{
    char name[128];
    char id[39]; // "wzkWht-tFcl-vR1S-50bC-gXcl-ATu2-2TKvkv"
    char status[64];
    uint8_t flags;
    uint64_t creation_time; // unix time
    char creation_host[16]; // "ubuntu"
    uint8_t segment_count;
    logical_volume_segment_t** segments;
};

typedef struct physical_volume physical_volumes_t;
struct physical_volume
{
    char name[128];  // "pv0"
    char id[39]; // "FKjNKr-Aq2l-507H-SjUe-o0iT-jIuG-Yez03z"
    char device[128]; // "/dev/sdc1" 或者 "/dev/sdb"之类
    uint8_t status[64];
    uint8_t flags;
    uint64_t dev_size;  // sector count
    uint64_t pe_start;  // relative sector count from the begining of partition or disk
    uint64_t pe_count;  // total bytes size : pe_count * extent_size * sector_size
};

typedef struct volume_group volume_group_t;
struct volume_group
{
    char name[128]; // max length
    char id[39]; // UUID which occupies 38 bytes,the last char is '\0'
    uint8_t seqno;
    char format[8]; // often 'lvm2'
    uint8_t status[64]; // LVM_STATUS
    uint8_t flags; // unkown what it is 
    uint32_t extent_size;
    uint32_t max_lv;
    uint32_t max_pv;
    uint32_t metadata_copies;
    uint8_t pv_count;
    physical_volumes_t** pvs;  // 事实上这是一个长度为pv_count的指针数组
    uint8_t lv_count;
    logical_volume_t** lvs; // 事实上这是一个长度为lv_count的指针数组
};

0x05: 项目小结

  之前只是交代了一下上下文。其实从这里才是真正的项目小结,总结一些lvm中的坑和项目上获得的其他经验:
  1. 在physical_volume结构体中有一个id字段,它是一个标识唯一一个pv的UUID号,但是它不是以-隔开的,大概是这个样子: 9LBcEB7PQTGIlLI0KxrtzrynjuSL983W,但是在volume_header结构体中pv的UUID是以-隔开的,比如9LBcEB-7PQT-GIlL-I0Kx-rtzr-ynju-SL983W,事实上他们是相等的。
  2. 当某个vg是有多个pv组成时,可以发现每个pv设备都可以读取到上述的一个配置文件,他们绝大部分内容都是相同的,除了 volume_header,表明当前是哪个pv设备。通过遍历vg下面每个lv的logical_volume_strip结构可以知道该vg依赖了哪些pv,然后再遍历volume_group中pvs成员可以得到该vg依赖的每个pv的uuid。
  3. 要完成lvm的解析最好阅读一遍libvslvm这个开源库,libyal的代码写的很谨慎。
  4. 通过lvm和ldm这个项目,把cgo玩的比较熟练了。
  5. 英文文档的阅读是作为一个开发者的基本能力。
  6. 事实上lvm远远比我现在完成功能更复杂,更强大的地方在于RAID还没进行研究,应该会在最近迭代进行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值