背景
- Libvirt是一个虚拟机管理的开源项目,它的实现涉及到对虚拟机任务的管理,为了提高Libvirtd服务并发执行API的效率,同时又能保证多线程访问同一个虚机数据结构的一致性,Libvirt基于不同场景设计实现了不同粒度的同步机制,本文主要分析这些同步机制的设计原理。
VM同步原理
virDomainObj是Libvirt对虚机数据结构的抽象,包含了通用信息(比如虚机xml加载到内存信息)和每个虚机私有的driver信息(比如qemu driver的私有qemuDomainObjPrivate)。当多个API同时发起,可能出现多线程并发修改VM信息的场景,virDomainObj的基类parent中包含一把互斥锁virMutex,多线程访问VM时通过这把锁来保证数据同步。下图是一个示例,解释两个Libvirt的APIqemuDomainGetInfo并发执行时的加锁顺序。

- 如上图所示,thread1是Libvirtd服务收到客户端的rpc调用后fork的线程,开始执行
qemuDomainGetInfo,这个API的功能是获取虚机的内存、CPU运行状态等信息,这些信息在VM的结构体中都有保存,因此API的主要逻辑就是查询VM的数据结构。 - 步骤如下:
- 通过
qemuDomainObjFromDomain接口获取vm结构体,过程中对virDomainObj.parent加锁 - 获取虚机总内存:virDomainObj.def->mem.total_memory
- 获取虚机状态:virDomainObj.state.state
- 获取proc文件系统下虚机Qemu进程运行时间
- 通过
virDomainObjEndAPI释放virDomainObj.parent锁
- 以上步骤中,所有Libvirt API都必须执行1、5这两部,保证所有API对VM数据结构的视图一致。
- 现在假设Libvirtd服务在API执行过程中又收到了客户端的rpc调用消息,针对同一个虚机开始执行
qemuDomainGetInfo,由于Libvirt API编程规范中要求在修改VM信息前必须先获取virDomainObj.parent锁,而此时virDomainObj.parent锁被先前的API持有,因此后来的API只能等待先前API执行结束,释放锁之后才能开始。
任务同步原理
- VM同步保证了虚机信息在多线程修改时保持一致,但有的时候,针对虚机的一些操作是比较耗时的,并且时间开销不在对数据结构的修改上,而是其它动作,比如执行QMP、QGA等命令或者执行迁移、备份这种操作。这个时候VM同步的接口会显得粒度太大,需要允许在以上这些操作的同时,其它API也能并发执行。因此,Libvirt设计了针对虚机的任务同步机制和接口并定义了一系列同步任务,对于同一个虚机而言,同一时间只允许有其中一个任务在执行。

本文深入剖析Libvirt虚拟机管理的同步原理,包括VM同步如何确保数据一致性,以及任务同步、异步任务和嵌套异步任务的设计与执行流程。通过实例详细阐述了不同任务类型在并发执行时的锁机制和信号量协调,展示了Libvirt如何高效并发处理API请求,同时保证任务执行顺序和数据安全。
最低0.47元/天 解锁文章
1103

被折叠的 条评论
为什么被折叠?



