1. Qemu的type_init是在constructor属性中执行的,也就是在main之前,也就是所有type_init的设备都会在main之前执行其type_register_static;
2. type_register_static会malloc TypeImpl数据结构,存放class_init/instance_init等回调函数和class_size/instance_size;
需要重点说下class_size:这是所注册设备Class数据结构的sizeof,比如
struct BusClass/pc中的PCMachineClass/struct PCIDeviceClass(称之为设备State)
而class_size和instance_size的意义都是为了Qemu框架为设备class alloc上述结构体的内存,
这些设备Class数据结构将会在其class_init被调用时传入。
这些设备class内存的alloc发生在设备被启用时,这是因为type_register_static调用时尚未确定传入参数,
并不能确定是否需要此设备。
另外一个instance_size:这是所注册设备State数据结构的sizeof,比如
struct Object/struct BusState/struct PCMachineState/struct PCIDevice等(称之为设备State)
而这些设备State数据结构将会在其instanct_init被调用时传入,如果一个设备的instance_size为0,
则此设备是虚拟设备。
设备State内存的alloc时机其实是与设备State相近的,下文会有详解。
3. 这里需要解说一下这个instance和class这两个相差不远的老伙计存在的意义,
我认为设计上instance(设备State)主要为了存放:
状态(如pci的irq_state)、属性(如pci的msix_cap/machine的各MemoryRegion);
而class最明显的标志就是,如果此设备有realize函数,则必在class。
class的alloc和class_init的调用都先于instance_init。
另外,设备可能仅有class或仅有instance。
4. 实际上type_register_static可以注册的函数不只有class_init和instance_init,我提供一个列表:
class_init()
instance_init()
instance_post_init()
罗列顺序即函数被调用顺序。
另外还有一个class_base_init()有些奇怪,它的调用发生在child device对parent device进行递归检查时,
也就是说,class_base_init是为自己可能存在的child device准备的,
同时要注意的是,如存在多级child,每一级child的触发,都会发生对所有parent->class_base_init的调用
这是一个被child device多次重复调用的接口
instance_finalize和class_finalize,本文不叙述
5. 关于class和instance的数据结构内存alloc、回调触发,都发生在object_new函数
object_new()是否调用由所注册设备被启用(创建虚拟机时通过machine型号、device参数、其他参数确定)决定,
调用时机是在设备初始化的阶段。
object_new首先根据设备所属类型(设备注册时传入的name字符串)查找TypeImpl,
并触发type_initialize(TypeImpl *);
type_initialize会先alloc class_size大小的内存,设备Class的内存由此存在,但是此内存是由多重含义的:
利用指向结构体的指针如同指向结构体中首元素这一C语言特性,
Qemu在struct xxxClass中做了套娃似的骚操作,如:
struct PCIDeviceClass {
struct DeviceClass {
struct ObjectClass {
struct TypeImpl {
const char *name;
...
struct PCMachineClass {
struct MachineClass {
struct ObjectClass {
struct TypeImpl {
const char *name;
...
这就存在了指向这段内存的指针其实是指向const char *name的,
也就是指向struct TypeImpl
也就是指向struct ObjectClass
...
这种便利性在Qemu被各中发扬扩散。
示例中结构体套娃都存在struct ObjectClass,这就是实质上的Qemu统一设备框架中的ObjectClass部分
这种套娃式继承是struct+name同步的,即type_register_static所给parent字符串必须与
struct xxxClass中的首元素一致,即必须为parent的struct,例如:
static const TypeInfo vfio_pci_dev_info = {
.name = Type_VFIO_PCI, /* "vfio-pci" */
.parent = TYPE_PCI_DEVICE, /* "pci-device" */
.instance_size = sizeof(VFIOPCIDevice),
...
对应的struct VFIOPCIDevice {
struct PCIDevice;
...
可以看到parent TYPE_PCI_DEVICE与首元素struct PCIDevice是需要一致的。
然后,type_initialize会递归的检查设备的parent(字符串)并先触发parent的type_initialize;
然后从当前device当前parent开始,一直到的最初代parent,全部进行class_base_init(如4.中描述)
最后调用当前device的class_init。
在type_initialize之后instance_size的内存同样被alloc,这段内存提供给了struct object,此处需要指出:
如同class中的struct ObjectClass,instance中存在struct object,是Qemu统一设备框架中的Object部分,
struct Object中包含struct ObjectClass。
同样给出instance套娃示例:
struct PCIDevice {
struct DeviceState {
struct Object {
struct ObjectClass {
struct TypeImpl {
const char *name;
...
struct PCMachineClass {
struct MachineState {
struct Object {
struct ObjectClass {
struct TypeImpl {
const char *name;
...
struct Object中的ObjectClass其实就是上方示例struct DeviceClass/struct MachineClass中的ObjectClass
额外的,TypeImpl中同样存由ObjectClass,在Qemu统一设备框架内TypeImpl实质上完成ObjectClass的传递。
随后开始的是instance_init的调用,首先递归检查设备的parent,并先触发parent的class_init;
然后当前Device的class_init得到调用。
最后一步是instance_post_init的调用,首先当前Device的instance_post_init被调用,
然后递归检查并设备parent的instance_post_init。