Linux对象管理机制
Linux对象管理机制
在linux内核中,采用对象机制来组织和管理系统单元。kobject表示基本对象单元,kset用来管理一组kobject,ktype用来定义对象行为。
此图来源于互联网
一、kobject
结构体如下所示,省略了一些暂时不关注的成员
struct kobject {const char *name; /* 每一个kobject对应/sys/下面的一个目录,而name则是目录名 */struct list_head entry; /* 用于在kset中将一串kobject链起来 */struct kobject *parent; /* 父节点,可以理解为父目录 */struct kset *kset; /* 当前kobject属于的kset */struct kobj_type *ktype; /* 当前kobject对应的ktype */struct kref kref; /* 引用次数,用来判断当前kobject是否还被使用 */unsigned int state_initialized:1; /* 如果为1,表示该kobject已经初始化 */unsigned int state_in_sysfs:1; /* 如果为1,表示该kobject已经添加到sysfs中 */unsigned int state_add_uevent_sent:1; /* 如果为1,表示该kobject已经发送了add uevent */unsigned int state_remove_uevent_sent:1; /* 如果为1,表示该kobject已经发送了remove uevent */unsigned int uevent_suppress:1; /* 如果为1,跳过发送uevent */
};struct kset {struct list_head list; /* 用来链接kset下的kobject成员 */struct kobject kobj; /* kset本质也是一种特殊的kobject,所以有该成员 */const struct kset_uevent_ops *uevent_ops; /* uevent的处理函数,当属于该set的kobject触发了uevent事件就会调用其中的函数 */
} __randomize_layout;struct kobj_type {void (*release)(struct kobject *kobj); /* 当kref为0时就会被调用 */const struct sysfs_ops *sysfs_ops; /* 定义sfsfs的读写接口 */struct attribute **default_attrs; /* kobj下的属性文件 */
};struct kset_uevent_ops {int (* const filter)(struct kset *kset, struct kobject *kobj); /* 决定是否允许该kobject发送uevent */const char *(* const name)(struct kset *kset, struct kobject *kobj); /* 自定义子系统名 */int (* const uevent)(struct kset *kset, struct kobject *kobj, /* 自定义的uevent环境变量 */struct kobj_uevent_env *env);
};
二、api函数分析
函数有删减,只保留核心逻辑
2.1 注册函数
kobject_init_and_addkobject_init--设置kobject->ktype成员的值kobject_init_internal--初始化kobject结构体中的链表、一些成员、引用次数为1kobject_add_vargkobject_set_name_vargs--设置kobj的namekobject_add_internal--设置父节点相关的信息,创建对应的目录kset_create_and_addkset_create--动态分配一个kset,并为kset->kobj设置name、parent、ktype等,设置uevent_opskset_registerkobject_add_internal--设置父节点相关的信息,创建对应的目录kobject_uevent--设置一个add的uevent事件static int kobject_add_internal(struct kobject *kobj)
{struct kobject *parent;parent = kobject_get(kobj->parent); /* 获取kobject的父kobject,并且父kobject的引用计数加1 */if (kobj->kset) {if (!parent)parent = kobject_get(&kobj->kset->kobj); /* 如果没有父kobject,则将kset作为父 */kobj_kset_join(kobj); /* 将kobject添加到kset的链表里面 */kobj->parent = parent; /*记录最终的父kobject,可能是原父,也可能是kset*/}error = create_dir(kobj); /* 创建kobject的目录以及对应ktype的attribute属性文件 */kobj->state_in_sysfs = 1; /* 设置kobject的state_in_sysfs为1,表示kobject已经添加到sysfs中 */
}
2.2 释放函数
void kobject_del(struct kobject *kobj)
功能是将该kobject从kset、parent、sysfs节点中删除
void kobject_del(struct kobject *kobj)
{struct kernfs_node *sd;/* 移除sysfs的目录,引用次数递减 */sd = kobj->sd;sysfs_remove_dir(kobj);sysfs_put(sd);kobj->state_in_sysfs = 0;kobj_kset_leave(kobj); /* 将kobject从kset链表中移除,kset对应的引用次数递减 *//*取消父子节点的绑定,parent引用递减*/kobject_put(kobj->parent);kobj->parent = NULL;
}
void kobject_put(struct kobject *kobj)
递减引用计数,当计数为0时调用release函数
void kobject_put(struct kobject *kobj)
{if (kobj) {kref_put(&kobj->kref, kobject_release);}
}
2.3 uevent函数
int kobject_uevent(struct kobject *kobj, enum kobject_action action);
的作用是像用户空间发送一个uevent事件
kobject_ueventkobject_uevent_envint kobject_uevent_env(struct kobject *kobj, enum kobject_action action,char *envp_ext[])
{/* 找到kobject所属的kset */top_kobj = kobj;while (!top_kobj->kset && top_kobj->parent)top_kobj = top_kobj->parent; if (!top_kobj->kset) {return -EINVAL;}/* 获取到kobject所属的kset的uevent_ops */kset = top_kobj->kset;uevent_ops = kset->uevent_ops; /* 如果uevent_suppress为1,跳过uevent的处理*/if (kobj->uevent_suppress) {return 0;}/* 如果filter返回0则跳过后续的处理 */if (uevent_ops && uevent_ops->filter)if (!uevent_ops->filter(kset, kobj)) {return 0;}/* originating subsystem */if (uevent_ops && uevent_ops->name)subsystem = uevent_ops->name(kset, kobj);elsesubsystem = kobject_name(&kset->kobj);/* 组装环境变量 */env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);devpath = kobject_get_path(kobj, GFP_KERNEL);retval = add_uevent_var(env, "ACTION=%s", action_string);retval = add_uevent_var(env, "DEVPATH=%s", devpath);retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);/* 调用uevent处理函数 */if (uevent_ops && uevent_ops->uevent) {retval = uevent_ops->uevent(kset, kobj, env);}switch (action) {case KOBJ_ADD:kobj->state_add_uevent_sent = 1; /* 标记已发送add事件 */case KOBJ_REMOVE:kobj->state_remove_uevent_sent = 1; /* 标记已发送remove事件 */case KOBJ_UNBIND:zap_modalias_env(env); /* 解绑时的清理操作 */}#if defined(CONFIG_NET)/* 通过netlink将内核的uevent广播给用户空间,暂不关注 */
#endif#ifdef CONFIG_UEVENT_HELPER/* 这一段是“用户态帮助程序”路径,用于在早期启动阶段(或未启用 netlink/broadcast 时)用用户态程序来处理 uevent,暂不关注 */
#endif
}
三、一个简单的demo
加载该模块后会在/sys/目录下生成如下结构的节点
/sys/kobject_parent/
├── kobj_config
├── kobject_child
│ └── kobj_config
└── kset_demo└── kobject_demo└── kobj_config
在模块insmod和rmmod时,会触发my_uevent_ops相关函数的调用。当echo和cat文件kobj_config时会触发obj_test_sysops相关函数的调用。当rmmod时,会触发obj_test_release函数的调用。
#include<linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/stat.h> MODULE_LICENSE("Dual BSD/GPL");/*对应于kobject的目录下的一个文件,Name成员就是文件名*/
struct attribute test_attr = {.name = "kobj_config",.mode = S_IRWXUGO,
};static struct attribute *def_attrs[] ={&test_attr, NULL,
}; /*当读文件时执行的操作*/
ssize_t kobj_test_show(struct kobject*kobject, struct attribute *attr,char *buf)
{ printk("have show.\n");printk("attrname:%s.\n", attr->name); printk("count %d.\n", kobject->kref.refcount);sprintf(buf,"%s\n",attr->name);return strlen(attr->name)+2;
} /*当写文件时执行的操作*/
ssize_t kobj_test_store(struct kobject*kobject,struct attribute *attr,const char *buf, size_t count)
{ printk("havestore\n"); printk("write: %s\n",buf); return count;
} //kobject对象的操作
struct sysfs_ops obj_test_sysops =
{.show = kobj_test_show, .store = kobj_test_store,
};/*release方法释放该kobject对象*/
void obj_test_release(struct kobject*kobject)
{ printk("eric_test: release .\n");
}/*定义kobject对象的一些属性及对应的操作*/
struct kobj_type ktype =
{.release = obj_test_release, .sysfs_ops=&obj_test_sysops, .default_attrs=def_attrs,
}; struct kobject kobj_parent;
struct kobject kobj_child;struct kset *kset_demo;
struct kobject kobj_demo;
static int my_filter(struct kset *kset, struct kobject *kobj)
{pr_info("[uevent_ops] filter called: kobj name = %s\n", kobject_name(kobj));return 1; // 返回 0 表示不过滤,允许事件
}static const char *my_name(struct kset *kset, struct kobject *kobj)
{pr_info("[uevent_ops] name called for: %s\n", kobject_name(kobj));return "my_kset_event";
}static int my_uevent(struct kset *kset, struct kobject *kobj,struct kobj_uevent_env *env)
{pr_info("[uevent_ops] uevent called for: %s\n", kobject_name(kobj));add_uevent_var(env, "MY_ENV_VAR=HelloWorld");return 0;
}static const struct kset_uevent_ops my_uevent_ops = {.filter = my_filter,.name = my_name,.uevent = my_uevent,
};static int kobj_demo_init(void)
{ printk("kboject demo init.\n");kobject_init_and_add(&kobj_parent,&ktype,NULL,"kobject_parent");kobject_init_and_add(&kobj_child,&ktype,&kobj_parent,"kobject_child");kset_demo = kset_create_and_add("kset_demo",&my_uevent_ops,&kobj_parent);kobject_init_and_add(&kobj_demo, &ktype, &kset_demo->kobj, "kobject_demo");kobj_demo.kset = kset_demo;kobject_uevent(&kobj_demo, KOBJ_ADD);return 0;
} static void kobj_demo_exit(void)
{ printk("kobject demo exit.\n");/* 先释放属于 kset 的 kobject,避免 kset 仍被占用 */kobject_put(&kobj_demo);/* 注销 kset(内部会对 kset->kobj 执行 kobject_del 与 kobject_put) */kset_unregister(kset_demo);/* 先删子节点,再删父节点,且必须在 del 后配对 put 才能真正释放 */kobject_del(&kobj_child); kobject_put(&kobj_child);kobject_del(&kobj_parent);kobject_put(&kobj_parent);
}module_init(kobj_demo_init);
module_exit(kobj_demo_exit);