我们在qemu2的qom系统分析(-)对象系统 一文中分析过qom系统如何通过TypeInfo注册对象,和对象的创建,继承,实现。
qom系统除了这些能力,还提供了引用计数能力,和属性设置,规范路径的能力。
规范路径的能力其实就是设置对象的child类型子属性,从而形成对象之间的一个树状结构。这样从根节点向下遍历,每个对象都有一个唯一的路径,所以就可以通过路径来寻找对象。
所以我们要想了解对象的规范路径如何实现就需要先看下对象的属性是如何实现的。
1 属性系统
首先什么是属性?其实就是对象的成员变量,成员变量要有名字,还要有值。所以也可以理解成key,value的形式。
qom对象支持的属性类型有:
- str
- bool
- int
- uint
- link
- qobject
在object.c里面对应设置这些属性的函数名称为object_property_set_* ,获取属性value的函数名称为object_property_get_, 其中就是类型的名称。其中link比较不好理解,其实对于link类型,key就是字符串,value就是对象对应的规范名称,要想通过规范名称找到对应的对象还需要进行查找。所以这其实类似一种软连接方式。
我们以bool类型举例来看看qom系统如何进行属性设置
void object_property_set_bool(Object *obj, bool value,
const char *name, Error **errp)
{
QBool *qbool = qbool_from_bool(value);
object_property_set_qobject(obj, QOBJECT(qbool), name, errp);
QDECREF(qbool);
}
qbool_from_bool创建一个bool类型的对象,通过object_property_set_qobject将这个bool对象设置进obj的属性里。 这其实是做了一个整合,在其他属性的设置中其实最后都将创建一个对应object类型的对象,最后将这个对象向上转型泛化为object类型传入object_property_set_qobject来进行属性设置。
void object_property_set_qobject(Object *obj, QObject *value,
const char *name, Error **errp)
{
Visitor *v;
v = qobject_input_visitor_new(value);
object_property_set(obj, v, name, errp);
visit_free(v);
}
ObjectProperty *object_property_find(Object *obj, const char *name,
Error **errp)
{
ObjectProperty *prop;
ObjectClass *klass = object_get_class(obj);
prop = object_class_property_find(klass, name, NULL);
if (prop) {
return prop;
}
prop = g_hash_table_lookup(obj->properties, name);
if (prop) {
return prop;
}
error_setg(errp, "Property '.%s' not found", name);
return NULL;
}
函数并不是直接将qboject作为对象属性设置给对象,而是用访问者模式中的访问者包果了一下该对象。这里object_property_set 函数的参数为访问者对象,访问者对象里面又持有属性对象。所以属性对象丢不了。我们继续看object_property_set函数如何甚至属性。
void object_property_set(Object *obj, Visitor *v, const char *name,
Error **errp)
{
ObjectProperty *prop = object_property_find(obj, name, errp);
if (prop == NULL) {
return;
}
if (!prop->set) {
error_setg(errp, QERR_PERMISSION_DENIED);
} else {
prop->set(obj, v, name, prop->opaque, errp);
}
}
object_property_find函数比较负责,首先调用object_class_property_find函数看下对象所属的类和成员是否有这个成员属性,如果有则可以设置,否则就直接返回了, 另外设置属性必须提供set方法(可以理解为必须提供类seter方法才能设置),如果没有提供则表明异常,打印错误。
在分析object_property_find前我们先看下怎么给类和对象添加属性。qom系统提供了两个函数。object_class_property_add_和object_property_add_ 分别用于给类和对象添加属性。*来表属性的类型,这里同样以bool类型为例进行分析。
void object_class_property_add_bool(ObjectClass *klass, const char *name,
bool (*get)(Object *, Error **),
void (*set)(Object *, bool, Error **),
Error **errp)
{
Error *local_err = NULL;
BoolProperty *prop = g_malloc0(sizeof(*prop));
prop->get = get;
prop->set = set;
object_class_property_add(klass, name, "bool",
get ? property_get_bool : NULL,
set ? property_set_bool : NULL,
property_release_bool,
prop, &local_err);
if (local_err) {
error_propagate(errp, local_err);
g_free(prop);
}
}
ObjectProperty *
object_class_property_add(ObjectClass *klass,
const char *name,
const char *type,
ObjectPropertyAccessor *get,
ObjectPropertyAccessor *set,
ObjectPropertyRelease *release,
void *opaque,
Error **errp)
{
ObjectProperty *prop;
if (object_class_property_find(klass, name, NULL) != NULL) {
error_setg(errp, "attempt to add duplicate property '%s'"
" to object (type '%s')", name,
object_class_get_name(klass));
return NULL;
}
prop = g_malloc0(sizeof(*prop));
prop->name = g_strdup(name);
prop->type = g_strdup(type);
prop->get = get;
prop->set = set;
prop->release = release;
prop->opaque = opaque;
g_hash_table_insert(klass->properties, g_strdup(name), prop);
return prop;
}
其实很简单,就是创建一个xxxProperty对象,然后插入到ObjectClass 的properties hash表中。 另外还可以设置属性的set和get方法。
Object的属性设置入法炮制,就不再分析了。读者如果自行分析可以看到可以添加name以[*]结尾的字符串,其实就是把
[*]替换为一个与现有属性名称不重复的数字进行添加。
下面来看object_property_find的实现
ObjectProperty *object_property_find(Object *obj, const char *name,
Error **errp)
{
ObjectProperty *prop;
ObjectClass *klass = object_get_class(obj);
prop = object_class_property_find(klass, name, NULL);
if (prop) {
return prop;
}
prop = g_hash_table_lookup(obj->properties, name);
if (prop) {
return prop;
}
error_setg(errp, "Property '.%s' not found", name);
return NULL;
}
ObjectProperty *object_class_property_find(ObjectClass *klass, const char *name,
Error **errp)
{
ObjectProperty *prop;
ObjectClass *parent_klass;
parent_klass = object_class_get_parent(klass);
if (parent_klass) {
prop = object_class_property_find(parent_klass, name, NULL);
if (prop) {
return prop;
}
}
prop = g_hash_table_lookup(klass->properties, name);
if (!prop) {
error_setg(errp, "Property '.%s' not found", name);
}
return prop;
}
object_property_find的实现首先先调用object_class_property_find函数,在ObjectClass的properties表中查找是否有同名属性,如果有则直接返回,这也就说明ObjectClass和Object不允许有重名的函数,这也很好理解,其实就是静态成员函数和非静态成员函数不能重名。object_class_property_find 函数则要一直顺着父类向上查找。
对于object_property_get_*方法也比较简单,读者自行分析。
到这里属性系统我们分析的就七七八八了。
2 规范路径
理解了属性系统规范路径就不是问题了。
我们先来看一个Object的特殊类型的Proptery
void object_property_add_child(Object *obj, const char *name,
Object *child, Error **errp)
{
Error *local_err = NULL;
gchar *type;
ObjectProperty *op;
if (child->parent != NULL) {
error_setg(errp, "child object is already parented");
return;
}
type = g_strdup_printf("child<%s>", object_get_typename(OBJECT(child)));
op = object_property_add(obj, name, type, object_get_child_property, NULL,
object_finalize_child_property, child, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto out;
}
op->resolve = object_resolve_child_property;
object_ref(child);
child->parent = obj;
out:
g_free(type);
}
这里为Object添加一个属性,属性的类型为type = g_strdup_printf(“child<%s>”, object_get_typename(OBJECT(child)));
也就是child<child_typename>
另外设置了child->parent = object. 但是并没有object_property_set_child函数,也就是没有办法给这个属性赋值,也就是说孩子知道父亲是谁,父亲只知道孩子的名字,却不知道孩子是谁,是不是很搞笑,其实不用但是,父亲可以通过孩子的名字找到它,这就是规范路径的作用。 通向我们也可以根据孩子知道它的名字(规范路径)
先来看根如何获取对象的规范路径
gchar *object_get_canonical_path(Object *obj)
{
Object *root = object_get_root();
char *newpath, *path = NULL;
while (obj != root) {
char *component = object_get_canonical_path_component(obj);
if (path) {
newpath = g_strdup_printf("%s/%s", component, path);
g_free(component);
g_free(path);
path = newpath;
} else {
path = component;
}
obj = obj->parent;
}
newpath = g_strdup_printf("/%s", path ? path : "");
g_free(path);
return newpath;
}
gchar *object_get_canonical_path_component(Object *obj)
{
ObjectProperty *prop = NULL;
GHashTableIter iter;
g_assert(obj);
g_assert(obj->parent != NULL);
g_hash_table_iter_init(&iter, obj->parent->properties);
while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) {
if (!object_property_is_child(prop)) {
continue;
}
if (prop->opaque == obj) {
return g_strdup(prop->name);
}
}
/* obj had a parent but was not a child, should never happen */
g_assert_not_reached();
return NULL;
}
static inline bool object_property_is_child(ObjectProperty *prop)
{
return strstart(prop->type, "child<", NULL);
}
object_property_is_child 判断一个属性是否为一个孩子属性。如果是的话,则持有该属性的对象就是路径的一部分。object_get_canonical_path 就是顺着最底层对象的parent向上遍历,找到每一级路径,直到找到根对象,最后来组装成对象的规范路径。
另外根据规范路径名称来找到对象的函数为object_resolve_path。
Object *object_resolve_path(const char *path, bool *ambiguous)
{
return object_resolve_path_type(path, TYPE_OBJECT, ambiguous);
}
Object *object_resolve_path_type(const char *path, const char *typename,
bool *ambiguousp)
{
Object *obj;
gchar **parts;
parts = g_strsplit(path, "/", 0);
assert(parts);
if (parts[0] == NULL || strcmp(parts[0], "") != 0) {
bool ambiguous = false;
obj = object_resolve_partial_path(object_get_root(), parts,
typename, &ambiguous);
if (ambiguousp) {
*ambiguousp = ambiguous;
}
} else {
obj = object_resolve_abs_path(object_get_root(), parts, typename, 1);
}
g_strfreev(parts);
return obj;
}
函数其实比较简单,就是从object_get_root()向下搜索路径,直到找到对应的obj为止,另外路径为相对路径的情况下,可能有个子路径重名,比如在/a/b/c 和/d/b/c中查找相对路径b/c 则就会出现重名的路径,通过ambiguousp参数作为传出值,来告诉调用者是否有冲突。
总结
最后总结一下
1 给对象添加属性使用函数object_property_add_*
可以使用name[*] 类型的字符串添加属性,系统会默认将* 替换为一个数字id,作为名称添加到属性里表(属性三要素 类型,名称,和值)。 类型如"int", “bool”,"str"等,对于子类型为child<$childname>.
另外名称不能和类属性重名也不能和其他属性重名
另外参数为 setter和geeter函数
2 给类添加属性(相当于静态变量)object_class_property_add_*
同1
3 设置属性值。 object_property_set_*
必须先添加了属性才能设置属性的值。
4 获取属性的值 object_property_get_bool_*
5 规范路径
- 根据规范路径查找对象object_resolve_path
- 根据对象获取规范路径object_get_canonical_path