标签:组成 查找 data 数组元素 win 功能 hot enc gem
关键术语:Qcow2 Header
typedef struct QCowHeader {
uint32_t magic;
uint32_t version;
uint64_t backing_file_offset;
uint32_t backing_file_size;
uint32_t cluster_bits;
uint64_t size; / in bytes /
uint32_t crypt_method; / 0 - 未加密;1 - AES加密 /
uint32_t l1_size; / XXX: save number of clusters instead /
uint64_t l1_table_offset;
uint64_t refcount_table_offset;//refcount table在img中的偏移
//refcount table所占用的cluster数目
uint32_t refcount_table_clusters;
//镜像中快照的个数
uint32_t nb_snapshots;
uint64_t snapshots_offset;
/ The following fields are only valid for version >= 3 /
uint64_t incompatible_features;
uint64_t compatible_features;
uint64_t autoclear_features;
uint32_t refcount_order;
uint32_t header_length;
} QEMU_PACKED QCowHeader;
Qcow2 Host cluster management
Qcow2维护refcount用以管理image中cluster的分配和释放,refcount作用等同于引用计数,代表了指定的cluster的使用状态:
0: 表示空闲
1: 表示已使用
大于等于2:表示已使用并且写访问必须执行COW操作
refcounts通过二级表(类似页表)来进行索引,第一级表称为refcount table,其大小可变、连续、占用多个cluster,其表项中每一个条目为指向第二级表的指针(相对于img file的offset),每个条目占64bits。
第二级表称为refcount block,每个refcount block占用1个cluster,表中每个条目为2个字节大小的refcount。
给定一个相对于img file的offset可以通过下面计算关系得到refcount:
refcount_block_entries = (cluster_size / sizeof(uint16_t))
refcount_block_index = (offset / cluster_size) % refcount_block_entries
refcount_table_index = (offset / cluster_size) / refcount_block_entries
refcount_block = load_cluster(refcount_table[refcount_table_index]);
return refcount_block[refcount_block_index];
Qcow2在qemu中的实现是作为块驱动实现,主要代码在:
block/qcow2.c
block/qcow2-refcount.c
block/qcow2-cluster.c
block/qcow2-snapshot.c
block/qcow2-cache.c
实现原理
Qcow2 img的操作在qemu中都是作为一种块设备的blockdriver来实现的,qcow2对应的bdrv_create注册的函数是qcow2_create,创建流程如下:
qcow2_create
qcow2_create2
bdrv_create_file
bdrv_create
bdrv_create_co_entry //qemu协程入口
raw_create
由于qcow2 image是以文件形式存在的,在Qcow2的底层仍需要通过文件操作写入实实在在的数据,在Qcow2管理结构上挂在了一个child管理结构,指向了bdrv_file的block driver,对应的API为raw_create,raw_open等。所以在层次划分上Qcow2 block driver完成了Qcow2内部格式的转换,比如Guest到host的cluster mapping,l1,l2表的建立,索引查找等。
在image file的创建流程上,首先写入header,offset为0,大小为cluster size
blk_pwrite(blk, 0, header, cluster_size);
接着写入一个refcount table和一个refcount block
blk_pwrite(blk, cluster_size, refcount_table, 2 cluster_size);
分配3个cluster,讲上面使用的3个cluster占用
qcow2_alloc_clusters(blk_bs(blk), 3 cluster_size);
最后根据header的最新信息更新image的header
qcow2_update_header(blk_bs(blk));
下面是snapshot的header信息,每一个snapshot都有一个header,而header中的l1_table_offset标示了该snapshot所使用的l1表。
typedef struct QEMU_PACKED QCowSnapshotHeader {
/ header is 8 byte aligned /
uint64_t l1_table_offset;//该snapshot所使用的l1表
uint32_t l1_size;
uint16_t id_str_size;
uint16_t name_size;
uint32_t date_sec;
uint32_t date_nsec;
uint64_t vm_clock_nsec;
uint32_t vm_state_size;
uint32_t extra_data_size; / for extension /
/ extra data follows /
/ id_str follows /
/ name follows /
} QCowSnapshotHeader;
为了将磁盘镜像地址映射到镜像文件偏移,需要经历以下几步:
qcow2_co_preadv
a. qcow2_get_cluster_offset:根据offset获取cluster内的数据,根据offset获取L1表的索引,再获取L2表,继续在获取L2 table表里的存放数据的地址,然后根据该值返回不同的类别。
enum {
QCOW2_CLUSTER_UNALLOCATED, //该cluster为分配
QCOW2_CLUSTER_NORMAL,
QCOW2_CLUSTER_COMPRESSED, //压缩类别
QCOW2_CLUSTER_ZERO //内容为全0
};
b.根据qcow2_get_cluster_offset的返回内别做不同处理:
case QCOW2_CLUSTER_UNALLOCATED:如果存在于back file中则从backfile中获取
case QCOW2_CLUSTER_NORMAL:bdrv_co_preadv直接读取文件对应位置
case QCOW2_CLUSTER_ZERO:直接设为全0
case QCOW2_CLUSTER_COMPRESSED:用qcow2_decompress_cluster读取
c bdrv_co_preadv读取数据
d. 循环a-c直到读取所有cluster
qcow2_co_pwritev
a. qcow2_alloc_cluster_offset:得到一个cluster,对已存在的cluster直接返回文件中的位置,对未分配的
cluster会先分配在返回其位置
|--> handle_alloc:为末分配的区域分配新的cluster或者需要copy-on-write
|-->do_alloc_cluster_offset:根据guest的地址分配cluster
|-->qcow2_alloc_clusters:分配地址,按照cluster偏移
|-->alloc_clusters_noref:分配虚拟地址,如果对应cluster的refcount为0,表示已找到末使 用的cluster
|-->update_refcount:更新索引
b. 若为加密方式则调用qcow2_encrypt_sectors
c. bdrv_co_pwritev写数据
d. 更新L2 Table qcow2_alloc_cluster_link_l2
e. 循环a-d直到写完所有cluster
ref table的管理
qcow2_get_refcount
refcount_block_cache字段的引入在于优化refcount的管理,当cache中数据已存在时不需要在读磁盘
Qcow2 Cluster mapping(Guest->Host)
Guest OS看到的只是virtual disk,操作的是Guest Cluster,所以Qcow2镜像另个重要功能就是管理Guest Cluster到Host Cluster的映射。
Guest Cluster到Host Cluster的映射关系也是通过一个二级表来管理,称为L1表和L2表,L1表大小可变、连续、占用多个cluster,其表项中每一个条目为指向L2的指针(相对于img file的offset),每个条目占64bits。
L2表占用一个cluster,每个条目占64bits.
给定一个相对于virtual disk的offset,可以通过下面计算关系得到Host Cluster offset:
l2_entries = (cluster_size / sizeof(uint64_t))
l1_index = (offset / cluster_size) / l2_entries
l2_index = (offset / cluster_size) % l2_entries
l2_table = load_cluster(l1_table[l1_index]);
cluster_offset = l2_table[l2_index];
return cluster_offset + (offset % cluster_size)
标签:组成 查找 data 数组元素 win 功能 hot enc gem
原文地址:http://blog.51cto.com/zybcloud/2159376