# 添加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内核模块。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://richardweiyang-2.gitbook.io/understanding_qemu/00-as/08-commit_mr.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
