📄 lvm.txt
字号:
any questions,send email to netxiong@eyou.com
相关文件
/drivers/md/lvm.c
/linux/lvm.h
lvm 的使用可以参看我的文档,其中它的设备操作函数集有open,close,ioctl,其中的open函数,只有在设备建立了文件系统以后,并且被mount到一个目录是才调用,平时不使用,只有read,write的函数进行处理。
**********************lvm的扇区分配***********************************
这是一个100M的lv的屏幕劫图。可以看到,一共204800个扇区,但是,有一部分的扇区是隐藏的,应该是一些配置信息存储的地方。一共可用的扇区数是200960个,由840个隐藏扇区,共240k.
lvm -- lvm_blk_open MINOR: 1 VG#: 1 LV#: 0 size: 204800
lv->lv_current_pe[0].pe = 4352
lv->lv_current_pe[1].pe = 12544
lv->lv_current_pe[2].pe = 20736
lv->lv_current_pe[3].pe = 28928
lv->lv_current_pe[4].pe = 37120
lv->lv_current_pe[5].pe = 45312
lv->lv_current_pe[6].pe = 53504
lv->lv_current_pe[7].pe = 61696
lv->lv_current_pe[8].pe = 69888
lv->lv_current_pe[9].pe = 78080
lv->lv_current_pe[10].pe = 86272
lv->lv_current_pe[11].pe = 94464
lv->lv_current_pe[12].pe = 102656
lv->lv_current_pe[13].pe = 110848
lv->lv_current_pe[14].pe = 119040
lv->lv_current_pe[15].pe = 127232
lv->lv_current_pe[16].pe = 135424
lv->lv_current_pe[17].pe = 143616
lv->lv_current_pe[18].pe = 151808
lv->lv_current_pe[19].pe = 160000
lv->lv_current_pe[20].pe = 168192
lv->lv_current_pe[21].pe = 176384
lv->lv_current_pe[22].pe = 184576
lv->lv_current_pe[23].pe = 192768
lv->lv_current_pe[24].pe = 200960
**********************************************************************
*********************LVM的磁盘结构************************************
(1)
PV在磁盘上的结构
每一个PV的前一K字节都保留给PV存储PV本身的一些数据。
紧接着的存储所有所在的vg的信息。
接下来是uuidlist(?)。
接下来是vg中所有的lv的信息。
最后是这个pv中所有pe的磁盘信息。
注意,这里的这些数据的分配基本上都是静态的分配,比如说,系统中最多的lv数目为n,那么
lv_disk_t区域的空间就是n*lv_disk_t这么大。虽然浪费一点,但是处理简单。
|<-------------------------------------whole disk------------------------------------------->|
|<---------------------------------------reseve------------------------------->|<----data--->|
|<--pv_disk_t-->|<--vg_disk_t-->|<--uuidlist-->|<--lv_disk_t-->|<--pe_disk_t-->|
**********************************************************************
************************lvm的内存数据结构之间的关系***************************
vg_t{
……
************
(struct pv_t)
pv[0]
pv[1]
……
pv[max_pv]
************
(struct lv_t)
lv[0]
lv[1]
……
lv[max_pv]
************
}
pv_t{
pe[0]
pe[1]
……
pe[n]-->pe_disk_t{
lv_num 这个pe所属的lv号
le_num 这个pe在所在的lv中的编号
}
}
lv_t{
lv_current_pe[0]
lv_current_pe[1]
……
lv_current_pe[n]-->pe_t {
dev
pe
read
write
}
}
******************************************************************************
**************************snap_shot设备和它的源设备的关系**************
snap_shot设备和普通的lvm设备的单位都是lv。如果一个普通的lv和一个snap_shot的lv
相关联了,那么在内核中相应的数据结构中就会有相应的处理。
对于被映射的lv设备,其lv结构中
typedef struct lv_v5 {
……
struct lv_v5 *lv_snapshot_prev;
struct lv_v5 *lv_snapshot_next;
……
}
由以上三个域将所有附属于这个lv的snap_shot类型的lv设备串联起来。当对这个源设备进行写
操作的时候,就会在这些候选的snap_shot的lv中选择一个,将源lv中的数据复制到选定的这个
snap_shot上。
对于每一个snap_shot类型的lv,同样在lv的结构中存在着一个域用于记录它是对应于哪一个源lv
typedef struct lv_v5 {
……
struct lv_v5 *lv_snapshot_org;
……
}
对于每一个snap_shot盘来说,在lv_t结构中,都有
lv_block_exception指针数组。
lv_remap_ptr
lv_remap_end
lv_snapshot_hash_table
四个结构,用来存储snap_shot盘中相应的块所对应的源盘数据。其实也就是相当于一个存储池。
每当一个源盘数据要进行snap_shot映射的时候,首先检查lv_remap_ptr的指针是否已经超过了
lv_remap_end的界限,如果没有超过,说明还有一部分的映射可以利用,就从lv_remap_ptr所
指向的地方取下来一个excption。进行一些处理以后(主要是填写源dev,r_sector)等信息,也就是
源盘上的信息。然后将它连接哈希表lv_snapshout_hash_table中,以便以后来进行查询。
一个典型的snap_shot和它的源设备的写过程如下
(1)源盘收到写请求。在snap_shot设备中选择一个作为写出设备。
(2)察看这个snap_shot的lv,如果在lv_snapshot_hash_table中已经存在一个映射。说明原来已经写过了。
(3)返回
(3)如果在lv_snapshot_hash_table中没有发现。说明要进行COW操作
(4)先从lv_block_exception中取出一个exception,如果没有exception,说明snap_shot没有空间了。
(5)取出来以后,将源盘数据的设备号和扇区号都写入到exception中
(6)将这个exception连接到哈希表中。以便以后查找。
(7)lv_remap_ptr加一。进行其它正常的写操作。
***********************************************************************
*****************关于snapshot的一致性的问题*****************************
lvm的snapshot的一致性问题不用过多的考虑。
(1)数据从源盘读出来,
(2)然后复制到snapshot盘,
(3)更新snapshot盘的纪录数据
这三个步骤都是异步的
也就是说,他们的运行不存在交叉问题,从而避免了一致性的问题。
只有在上述三个步骤都完成以后,新的数据才可以被写入到源盘上。
************************************************************************
********************snapshot的组织形式和结构*****************************
(1)
对于是snapshot的lv来说,其基本组织形式和普通的lv没有什么区别,不过,由于需要纪录
源盘的数据,所以它按照下面的方式进行组织。
snapshot盘和其它的块设备不太一样,他是按照chunk_size进行逻辑上的划分的,也就是说,
对于数据从源盘复制到snapshot盘,其数据块的大小是按照snapshot盘上的chunk_size为单位
进行复制的。这个chunk_size和一般块设备意义上的chunk不是很一样,这个chunk只是数据复制的单位。
对于不是snapshot的lv,他们的逻辑块大小是按照blk_size[][],blk_sizesize[][]这样的数组来定义的。
也就是说,上层的文件系统或者其它的设备看到的是以blk_size所标记的块大小的设备。他们的读取单位
也是这两个结构所规定的。也就是说,bh传下来的block_nr就是按照这个单位进行计量的
对于每一个le,一般大小为4M,其内部包含若干个chunks,系统经过计算,可以确定出一个le中
可同时容纳多少个chunks和他们的结构标志lv_COW_table_disk_t,一个lv_COW_table_disk_t对应
一个chunk,所有的lv_COW_table_disk_t结构都安排在le的最前头,所有的数据块都紧接在这些
lv_COW_table_disk_t结构后面。但是,这样计算的话,可能就会产生空洞,也就是必定有一块
chunk不能被填满。这也没有办法。
整个snapshot盘的组织形式如下所示
|------------------------------------------|
|-----|------------------------------| |
| | \|/ \|/
|<-1->|<-1->|……|<-1->|<-1->|……|<----2---->|<----2---->|<----2---->| … ->|
|<----3---->|……|<----3---->|
|<-----------2-------------->|
|<----------------------------------------4---------------------------------->|
1---lv_COW_table_disk_t结构
2---一个lv_chunksize大小的数据块,用来存放一个数据块,或者存放若干lv_COW_table_disk_t结构
3---这个snapshot所在设备的块,它可以容纳若干个1
4---表示一个pe或者le
系统在进行写lv_COW_table_disk_t的时候,是按照这个结构所在的设备的块大小进行写操作的。
也就是按照3所标示的范围进行写操作。
但是同时,如果进行正常的COW操作的时候,其读写的范围是以2为单位进行的,也就是按照这个
snapshot的设备的逻辑块大小进行读写。
同时对于内存中的lv_block_exception_t结构数组来说,它的数量是snapshot中所有的le的数量
乘上一个le中所能容纳的chunks的数目。所以,lv_block_exception_t得数目和le没有什么必然的
关系。
*************************************************************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -