openrisc中ram对象的生成过程
QEMU利用QOM来对对象进行抽象,用它来对各种资源进程抽象、管理(创建、配置、销毁)。即QEMU内部实现面向对象机制的方法。
QOM机制有如下三个部分:
- 类型注册,涉及函数有
type_init
、register_module_init
、type_register
- 生成
TypeImpl
,通过TypeImpl
就可以知道如何初始化一个类 - 相当于告诉qemu这个类如何定义,如:
- 类名是什么:
.name
- 类实例化调用什么函数:
.instance_init
- 类自己的特有方法、对父类如何继承等:
.class_init
- 类名是什么:
- 生成
- 类型的初始化
type_initialize
- 类型定义告诉了QEMU怎么初始化类,类型初始化就根据定义的内容把类创建出来
- 对象的初始化
- 通过上面创建好的类来实例化对象
MemoryRegion也是一个QOM对象,在./softmmu/memory.c
进行了类型注册:
1 | static const TypeInfo memory_region_info = { |
其中类名TYPE_MEMORY_REGION
展开为qemu:memory-region
,每个MemoryRegion类的实例通过memory_region_initfn
构造,通过memory_region_finalize
销毁。通过以上信息QEMU就能知道如何创建MemoryRegion类,以及MemoryRegion类实例该如何生成。
MemoryRegion可以分为如下几类
- 根级MemoryRegion
- 没有自己的内存,用于管理subregion,如
system_memory
- 通过
memory_region_init
初始化
- 没有自己的内存,用于管理subregion,如
- 实体MemoryRegion
- 有具体的内存,从QEMU进程地址空间分配内存
- 通过
memory_region_init_ram
初始化,实际由qemu_ram_alloc
分配实际内存。如RAM、ROM、ROM device等
- 别名MemoryRegion
- 表示实体mr的不同分段,没有自己的内存,是实体mr的一部分
- 通过
memory_region_init_alias
初始化 - 通过
alias
成员 指向实体MemoryRegion ,alias_offset
表示该别名mr在实体内存中的偏移量
ram作为根级MemoryRegion,也是MemoryRegion类的一个实例,通过memory_region_init_ram
创建和初始化。
openrisc中对其ram的构造方法如下:
1 | // 函数原型 |
可见对象名将要设置为openrisc.ram
,memory_region_init_ram
的调用关系为:
1 | memory_region_init_ram() |
memory_region_init_ram
其底层也是调用memory_region_init
做通用MemoryRegion的初始化,只不过在上层,如memory_region_init_ram_shared_nomigrate()
中会针对ram做一些定制的操作。
memory_region_init()
主要有两部
1 | void memory_region_init(MemoryRegion *mr, |
object_initialize(mr, sizeof(*mr), TYPE_MEMORY_REGION);
- 为ram生成MemoryRegion类对象/实例
memory_region_do_init(mr, owner, name, size);
- 对实例化后的MemoryRegion对象进行初始化
object_initialize
object_initialize()
会通过object_initialize_with_type()
根据传入的typename符串找到类的描述,从而初始化一个QOM对象。
这里typename为TYPE_MEMORY_REGION
宏,展开为qemu:memory-region
,也就是MemoryRegion类注册的类名。通过类名找到了MemoryRegion类,用于做MemoryRegion类的实例化。这里是对openrisc.ram
实例化
1 | void object_initialize(void *data, size_t size, const char *typename) |
object_initialize_with_type()
内容如下:
1 | static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type) |
object_init_with_type
会调用自身的实例化函数并递归地调用父类的实例化函数。对于MemoryRegion
类的对象,其实例化函数为memory_region_initfn()
1 | static void memory_region_initfn(Object *obj) |
memory_region_initfn
会给该MemoryRegion对象做如下基本设置:
- 赋值默认操作函数
unassigned_mem_ops
- 添加link类型的container属性
- 每个mr都可以是其他mr的container
- link属性表示一种连接关系,表示一种设备引用另一种设备
- 添加priority属性
- 添加size属性
memory_region_do_init
通过上面的步骤就生成了一个MemoryRegion实例,通过memory_region_do_init
再对该MemoryRegion实例进程初始化(本例是是openrisc.ram的初始化,以下就称为openrisc.ram对象或openrisc.ram实例)
memory_region_do_init
对实例初始化,如设置名称、设置owner执行父对象、添加属性等:
1 | static void memory_region_do_init(MemoryRegion *mr, |
openrisc.ram的初始化过程传入的owner为NULL,这样就会分配一个叫”/unattached”的Object作为其owner,然后将openrisc.ram实例作为owner的child属性:
然后通过qemu monitor查看qom信息:info qom-tree
。就会发现openrisc.ram对象是unattached
对象的一个child属性
抽象
接口
类注册
使用TypeInfo结构来描述一个类,其中一些个重要成员如下:
parent
,父类名name
,该类型的类名class_size
,该类的大小class_init
,该类的初始化函数instance_size
,该类的实例对象的大小instance_init
,该类的实例的实例化函数instance_finalize
,该类实例的销毁函数
1 | struct TypeInfo |
定义好一个类后需要使用type_register_static(YOUR_TYPEINFO);
将类注册到全局hash表,用于根据类名构造类和类实例。
type_register_static
可以将类信息注册,但一般不直接调用或者用到时在调用。而是使用type_init(YOUR_REGISTER)
让所有类注册在main之前完成。type_init
是个宏,其又是module_init
宏,module_init
宏如下:
1 |
|
module_init
宏通过添加编译器属性__attribute__((constructor))
保证注册函数会在main
函数前调用。
类似的宏还有block_init
、opts_init
、trace_init
等
类初始化
通过定义TypeInfo
和注册,仅仅是告诉了qemu要怎么构造类,要生成类的实例还要调用type_initialize
将类创建出来。即类的初始化通过type_initialize
完成。
其先判断ti->class
如果非空说明类已经初始化过了。因此使用object_initialize_with_type
其实是类对象的一种懒加载方法。
1 | static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type) |
其如果是第一次执行则会进入type_initialize
,发现
不过大部份类初始化在创建虚拟机时就通过下面步骤初始化好了。所以object_initialize_with_type
的type_initialize
更多还是在保证类确实已经创建了。没有创建再调用创建。
1 | qemu_init |
通过类实例化一个对象
1 | void object_initialize(void *data, size_t size, const char *typename) |
- data用于接受要实例化的对象
- 如
mr
- 如
- size表示该实例的大小
- 如
sizeof(*mr)
- 如
- typename表示类名
其内部有的核心调用为:
1 | |- TypeImpl *type = type_get_by_name(typename); |
object_initialize
会根据类名找到对应的类,然后通过object_initialize_with_type
实例化,其中实例化的核心是object_init_with_type(obj, type)
。它会根据类的实例化函数instance_init
将对象实例化,并递归的实例化父类。
而object_post_init_with_type
是处理对象实例化后的一些通过,会根据类的instance_post_init
函数完成。
初始化对象细节
像MemoryRegion类,其对象除了基础的实例化还有一些细节需要初始化。这就要为什么memory_region_init()
是这样的:
1 | void memory_region_init(MemoryRegion *mr, |
模仿MemoryRegion对象的构建方法,用户可以对自己的对象构造方法进一步封装分别实例化对象和补充细节。对应于memory_region_init
中的的object_initialize
和memory_region_do_init
以MemoryRegion对象为例,其memory_region_do_init
为MemoryRegion对象增加了owner
、name
域,还将对象本身添加到了父对象的child属性中。
属性
为了对对象进行管理,每种类型的对象都增加了 属性。类属性在ObjectClass
(所有QOM对象的父类)的properties
域中。
1 | struct ObjectProperty |
每种具体的属性都有一个结构体描述它。如LinkProperty
结构体表示link类型的属性,StringProperty
表示字符串类型的属性,BoolProperty
表示bool类型的属性。
LinkProperty
属性有个Object **targetp
成员,用于连接两个对象
属性添加通过object_property_add
实现,其是对object_property_try_add
的封装,object_property_try_add
多一个参数errp
用于保存错误代码
1 | ObjectProperty * |
其作用就是给下面些个域赋值。
obj
表示要添加属性的对象name
表示要属性名type
表示属性类型名- 通常格式为
namespace<sub-namespace>
,推荐用-
连接词
- 通常格式为
get
表示读属性用到方法set
表示写属性用到的方法release
表示将属性从对象中移除时调用的方法opaque
表示属性一个具体的属性对象
object_property_add_link
和object_property_add_child
仅是对object_property_add
封装以实现具体细节。
如object_property_add_link
会创建一个LinkProperty
对象传入object_property_add
,然后让属性的opaque
域指向该LinkProperty
对象。而object_property_add_child
则直接将qom对象传入。
设备间通过属性交互
- link/child属性
- link属性表示一种连接关系,表示一种设备引用另一种设备
- child属性表示对象之间的从属关系。对象的child属性执行子对象