解决Ubuntu18.04开机启动缓慢
本篇博客解决了Ubuntu18.04开机启动过程中,长时间停留在Ubuntu LOGO,再进入桌面系统的登录界面。
本篇博客解决了Ubuntu18.04开机启动过程中,长时间停留在Ubuntu LOGO,再进入桌面系统的登录界面。
本篇博客介绍了nouveau安装以及卸载nouveau遇到的Error: Module nouveau is in use 问题。
本篇博客记录如何使用 Gitlab 服务和邮件配置。
Gitlab 是 Git 服务端的集成管理平台,它拥有与Github类似的功能。 本篇博客记录如何在 Ubuntu 16.04 上搭建 Gitlab 服务。
苦于看内核驱动源码的烦恼,Doxygen工具很好的解决了此问题。 Doxygen可以很好的构建出类层次结构和全局变量,不同的用户定义类型,以及函数调用图分析等等。
debugfs
虚拟文件系统是一种内核空间与用户空间的接口,基于libfs库实现,专用于开发人员调试,便于向用户空间导出内核空间数据。
分析QEMU源码中的 virtconsole
字符设备文件,选择
QEMU v2.12.0
版本。
本篇博客主要讲述了QOM编程的教程,翻译了QEMU Object Model Style Guide 。
本文档是QOM的分步教程。 这里面没有提供具体的API,代码中包含内联文档和API详细信息可在相应的头文件中找到。
QEMU广泛使用面向对象编程。 但是由于QEMU是用C语言编写的,因此这些OOP概念通常使用非常不同的机制来实现相同的目标。 QOM的目标是为QEMU中的所有OOP使用单一基础架构,提高一致性,并且可以长期保持可维护性。
QOM提供了一个通用的基础设施:
虽然QOM有很多高级概念,但主要的设计目标是使简单的概念易于实现。
理解QOM最简单的方法就是举例子。 这是创建从Object派生的新类型作为父类的典型示例。 在此示例中,所有代码都将存在于单个C源文件中。
1 | #include "qemu/object.h" |
这是包含核心QOM基础结构的头文件。 它具有最小的依赖性,便于单元测试。
1 | #define TYPE_MY_TYPE "my-type" |
所有QOM类型都应至少定义两个宏。 第一个宏是类型Type名称的符号版本。
它应该始终采用TYPE_ + upper(typename)
的形式。
类型名称通常应遵循QAPI的命名规则,这意味着破折号-
优先于下划线
_
。 第二个宏是一个转换宏。
第一个参数是类型结构,其余参数是不言而喻的。
即使C文件当前未使用强制转换宏,也应始终遵循此形式。 1
2
3
4
5
6
7
8
9typedef struct MyType MyType;
struct MyType
{
Object parent_obj;
/*< private >*/
int foo;
};
在声明结构时,应使用前向声明。
由于在定义类时需要它,这对于一致性很有用。
第一个元素必须是父类型,并且应该命名为parent_obj
或
parent
。
使用QOM类型时,应避免直接访问此成员,而是依赖于转换宏。
转换宏隐藏了实现中的继承层次结构。
这使得通过更改层次结构而不必改变许多地方的代码,可以更容易地重构代码。
1
2
3
4
5
6
7
8
9
10
11
12static TypeInfo my_type_info = {
.name = TYPE_MY_TYPE,
.parent = TYPE_OBJECT,
.instance_size = sizeof(MyType),
};
static void register_types(void)
{
type_register_static(&my_type_info);
}
type_init(register_types);
必须在QOM基础结构中注册所有QOM类型。 注册后,用户可以枚举类型,创建对象以及与对象交互,而无需任何其他代码。
所有类型都必须设置 name
和 parent
参数。
类型宏
应始终用于这些参数。 几乎所有类型都应该设置
instance_size
参数,如果没有指定,它将从其父项继承。
最后,应提供模块初始化函数。 此处显示的命名约定应该在所有新代码中使用。
通常,一个C文件应该注册一种类型。 此规则有许多有效的例外,但只要有可能,类型应拆分为单独的C文件。
与QOM的下一个最常见的交互是创建一个将从另一个类型Type继承的类型Type。
这通常涉及添加类并实现可以覆盖子类的虚方法。
以下差异显示了我们需要扩展前一个示例以允许使用多态继承的更改。
1
2
3
4
5@@ -1,10 +1,25 @@
+#ifndef QEMU_MY_TYPE_H
+#define QEMU_MY_TYPE_H
+
#include "qemu/object.h"
1 | #define TYPE_MY_TYPE "my-type" |
添加类时,我们需要在类型定义中再添加两个宏。 第一个宏是类转换宏。
这看起来非常类似于对象强制转换宏,而是将类作为参数。
我们添加的第二个宏允许用户从对象Object获取类Class指针。
方法调度需要最后一个宏。 1
2
3
4
5
6
7
8
9 typedef struct MyType MyType;
+typedef struct MyTypeClass MyTypeClass;
+
+struct MyTypeClass
+{
+ ObjectClass parent_klass;
+
+ void (*bar)(MyType *obj, int foo);
+};1
2
3
4
5
6
7
8
9 struct MyType
{
@@ -14,10 +29,35 @@ struct MyType
int foo;
};
+void my_type_bar(MyType *obj, int foo);
+
+#endif1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29+
+static void my_type_default_bar(MyType *obj, int foo)
+{
+ /* do nothing */
+}
+
+void my_type_bar(MyType *obj, int foo)
+{
+ MyTypeClass *mc = MY_TYPE_GET_CLASS(obj);
+
+ mc->bar(obj, foo);
+}
+
+static void my_type_class_init(ObjectClass *klass, void *data)
+{
+ MyTypeClass *mc = MY_TYPE_CLASS(klass);
+
+ mc->bar = my_type_default_bar;
+}
+
static TypeInfo my_type_info = {
.name = TYPE_MY_TYPE,
.parent = TYPE_OBJECT,
.instance_size = sizeof(MyType),
+ .class_size = sizeof(MyTypeClass),
+ .class_init = my_type_class_init,
};
static void register_types(void)TypeInfo
中指定类的大小。
我们还需要提供一个初始化类的函数。
只为任何类型创建和初始化类一次,因此无论此类型创建了多少对象,都将调用此函数一次。
类初始化函数应遵循 typename +'_ class_init'
的命名约定。
类初始化函数应该将 klass参数强制转换为适当的类型,然后适当地重载方法。
在这个例子中,我们将方法初始化为一个没有用处的虚函数。
这是因为'foo'是一个虚方法,这意味着如果基类不想覆盖行为,则不需要实现该函数。
如果我们没有初始化该方法,那么该函数将是一个纯虚方法,这意味着子类必须实现该函数。 QOM无法强制执行此要求,因此应在包装函数中小心检查NULL。
包装函数只是调度方法。 除了调度方法之外,它不应该实现任何逻辑或行为。 包装器函数可以检查NULL并返回错误或断言。
大多数QOM用户不会实现从 TYPE_OBJECT
派生的对象。
相反,通常QOM用户将从 TYPE_DEVICE
或其他一些基类派生,并且还必须实现虚拟方法。
在此示例中,我们将 MyType
更改为从
TYPE_DEVICE
继承,然后填充所需的纯虚方法。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17@@ -16,14 +16,14 @@ typedef struct MyTypeClass MyTypeClass;
struct MyTypeClass
{
- ObjectClass parent_klass;
+ DeviceClass parent_klass;
void (*bar)(MyType *obj, int foo);
};
struct MyType
{
- Object parent_obj;
+ DeviceState parent_obj;
/*< private >*/
int foo;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21@@ -45,16 +45,27 @@ void my_type_bar(MyType *obj, int foo)
mc->bar(obj, foo);
}
+static int my_type_realize(DeviceState *dev)
+{
+ MyType *my = MY_TYPE(dev);
+
+ my->foo = 1;
+
+ return 0;
+}
+
static void my_type_class_init(ObjectClass *klass, void *data)
{
MyTypeClass *mc = MY_TYPE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
mc->bar = my_type_default_bar;
+ dc->init = my_type_realize;
}TYPE_DEVICE
有一个纯虚方法
init
,这有点用词不当。 init
方法在构造之后但在guest虚拟机第一次启动之前调用。
在QOM命名法中,我们称之为 realize
。
在某个时间点,TYPE_DEVICE
将被重构以重命名
init
方法以实现,但是现在,我们必须忍受这种不一致。
1
2
3
4
5
6
7 static TypeInfo my_type_info = {
.name = TYPE_MY_TYPE,
- .parent = TYPE_OBJECT,
+ .parent = TYPE_DEVICE,
.instance_size = sizeof(MyType),
.class_size = sizeof(MyTypeClass),
.class_init = my_type_class_init,
QDev要求通过 init
和 exit
方法进行所有初始化和破坏。
由于QDev没有构造函数和析构函数的概念,因此实现链接的类型通常以不一致的方式完成。
作为 TypeInfo
结构的一部分,QOM有一个
instance_init
和 instance_finalize
方法,它们分别充当构造函数和析构函数。
这些函数从子类开始调用,并通过QOM处理类型层次结构。
任何可以独立于用户提供的状态进行初始化的状态都应该初始化为构造函数的一部分。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33@@ -33,6 +33,13 @@ void my_type_bar(MyType *obj, int foo);
#endif
+static void my_type_initfn(Object *obj)
+{
+ MyType *my = MY_TYPE(obj);
+
+ my->foo = 1;
+}
+
static void my_type_default_bar(MyType *obj, int foo)
{
/* do nothing */
@@ -47,10 +54,6 @@ void my_type_bar(MyType *obj, int foo)
static int my_type_realize(DeviceState *dev)
{
- MyType *my = MY_TYPE(dev);
-
- my->foo = 1;
-
return 0;
}
@@ -69,6 +72,7 @@ static TypeInfo my_type_info = {
.instance_size = sizeof(MyType),
.class_size = sizeof(MyTypeClass),
.class_init = my_type_class_init,
+ .instance_init = my_type_initfn,
};
static void register_types(void)foo
可以在不依赖于用户提供的状态的情况下进行初始化,因此我们可以将该逻辑完全移至构造函数。
不幸的是,DeviceState
init函数必须保留,因为它是纯虚拟的,但它现在是微不足道的。
QEMU中大多数对象的共同特性是希望允许用户在初始创建期间或在运行时调整对象的参数。 属性Properties提供了执行此操作的通用框架。
属性丰富而复杂,这里不会详尽介绍。 有关详尽的文档,请参阅
qemu/object.h
头文件中的文档。
Most interactions with properties will happen through convenience
functions that make adding properties easier for typical users. In the
case of our example, we'll add properties using the qdev static property
interface.
大多数与属性的交互都是通过便利函数实现的,这使得典型用户更容易添加属性。
在我们的示例中,我们将使用qdev静态属性接口添加属性。 1
2
3
4
5
6@@ -27,6 +27,7 @@ struct MyType
/*< private >*/
int foo;
+ int max_vectors;
};1
2
3
4
5
6
7
8
9
10
11
12
13 void my_type_bar(MyType *obj, int foo);
@@ -54,9 +55,20 @@ void my_type_bar(MyType *obj, int foo)
static int my_type_realize(DeviceState *dev)
{
+ MyType *mt = MY_TYPE(dev);
+
+ if (mt->max_vectors > 100) {
+ return -EINVAL;
+ }
+
return 0;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16+static Property my_type_properties[] = {
+ DEFINE_PROP_INT("max-vectors", MyType, max_vectors, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void my_type_class_init(ObjectClass *klass, void *data)
{
MyTypeClass *mc = MY_TYPE_CLASS(klass);
@@ -64,6 +76,7 @@ static void my_type_class_init(ObjectClass *klass,
mc->bar = my_type_default_bar;
dc->init = my_type_realize;
+ dc->props = my_type_properties;
}
static TypeInfo my_type_info = {TYPE_DEVICE
类中的静态类变量注册静态属性。基类可以使用此方法添加静态属性,子类将自动继承它们。
QOM中其他常见类型的属性是子属性和链接属性。
与静态属性一样,有一些特殊的帮助器可以将这些属性添加到对象中。
1
2
3
4
5
6
7
8
9
10@@ -25,6 +25,9 @@ struct MyType
{
DeviceState parent_obj;
+ Pin *in;
+ Pin out;
+
/*< private >*/
int foo;
int max_vectors;
首先,我们必须在该对象中添加将保存属性的struct成员。 链接是指向另一个对象的指针,在C中表示为指针。子属性是嵌入对象,通过在对象结构中嵌入struct成员来表示。
子属性的生命周期与父对象相关联。 IOW,当MyType的一个对象被销毁时,嵌入其中的'out'对象将被自动销毁。
链接属性将保存对其指向的对象的引用,但不控制它指向的对象的生命周期。
也就是说,当MyType的一个对象被破坏时,'in'指向的对象不一定会被破坏,尽管它的引用计数会减少。
1
2
3
4
5
6
7
8
9
10
11
12
13@@ -39,6 +42,11 @@ static void my_type_initfn(Object *obj)
MyType *my = MY_TYPE(obj);
my->foo = 1;
+
+ object_initialize(&my->out, TYPE_PIN);
+ object_property_add_child(obj, "out", OBJECT(&my->out), NULL);
+
+ object_property_add_link(obj, "in", TYPE_PIN,
+ (Object **)&my->in, NULL);
}
static void my_type_default_bar(MyType *obj, int foo)
本篇博客整理Linux内核的内存分配相关知识,包括 kmalloc
...(补充)