添加MemoryRegion
本章我们将以添加MemoryRegion为线索,动态得考察各个数据结构及其之间的变化。
那添加MemoryRegion的线索是谁呢? 就是这个
memory_region_transaction_commit()
那就以此开始。
精简的call flow
让我们先从全局观察这个函数,获得一个全局的概念。
memory_region_transaction_commit(), update topology or ioeventfds
flatviews_reset()
flatviews_init()
flat_views = g_hash_table_new_full()
empty_view = generate_memory_topology(NULL);
generate_memory_topology()
MEMORY_LISTENER_CALL_GLOBAL(begin, Forward)
address_space_set_flatview()
address_space_update_topology_pass(false)
address_space_update_topology_pass(true)
address_space_update_ioeventfds()
address_space_add_del_ioeventfds()
MEMORY_LISTENER_CALL_GLOBAL(commit, Forward)
从上面的精简版来看,添加一个MemoryRegion需要做如下几件事:
flatviews_reset: 重构所有AddressSpace的flatview
MEMORY_LISTENER_CALL_GLOBAL(begin, Forward)
address_space_set_flatview: 根据变化添加删除region
address_space_update_ioeventfds: 根据变化添加删除eventfd
MEMORY_LISTENER_CALL_GLOBAL(commit, Forward)
所以总的来讲也就这五个步骤,其中第一个步骤就是将MemoryRegion转换为FlatView,而其余的四个步骤都是根据FlatView的变化做出相应的调整。
MemoryListener
为了辅助地址空间的动态变化,还需要了解一个数据结构 MemoryListener。
MemoryListerner
+---------------------------+
|begin |
|commit |
+---------------------------+
|region_add |
|region_del |
+---------------------------+
|eventfd_add |
|eventfd_del |
+---------------------------+
|log_start |
|log_stop |
+---------------------------+
有经验的朋友到这估计猜出来了,这个就是一个回调函数的集合,用在必要的时间点调用到这些钩子。
那在什么地方去调用这些回调函数呢?最主要的一共有两个宏定义
MEMORY_LISTENER_CALL_GLOBAL
MEMORY_LISTENER_CALL
这两个宏定义长得非常像,只有一点点的差别。那就是调用的MemoryListener的链表不同。
在memory_listener_register注册MemoryListener的时候,会注册到两个地方
memory_listeners
as->listeners
也就是一个是全局的链表,一个是AddressSpace自身的链表。
所以我们可以总结一下添加MemoryRegion时需要做的操作
根据新的MemoryRegion树生成FlatView
比较新旧FlatView调用MemoryListener进行修改
以KVM为例
最后我们以KVM为例,看看kvm是如何将客户机的内存通知给内核模块的。
既然知道了上述的流程概述,那么就知道重点是看kvm注册的MemoryListener的回调函数是什么。
查看代码,发现有两个重要的成员是:
kml->listener.region_add = kvm_region_add;
kml->listener.region_del = kvm_region_del;
那我们以region_add为例,看看添加一个MemoryRegion时做的工作。
kvm_region_add
kvm_set_phys_mem
kvm_set_user_memory_region
kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
所以最后是调用了KVM_SET_USER_MEMORY_REGION这个ioctl将qemu用户态的内存传给了kvm内核模块。
Last updated
Was this helpful?