每一个kobject对象都会关联一个sysfs文件目录,本节重点关注如何将kobject对象与sysfs文件系统关联起来,关注kobject对象默认的属性文件操作接口
1.kobject
首先看一下kobject的结构体定义
include/linux/kobject.h
struct kobject {
const char *name;
struct list_head entry;
struct kobject *parent;
struct kset *kset;
struct kobj_type *ktype;
struct kernfs_node *sd; /* sysfs directory entry */
struct kref kref;
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
struct delayed_work release;
#endif
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
};
各参数的意义为:
/*这个kobject对象的名字*/
const char *name;
/*链表结点,让kset对象将kobject串起来进行管理*/
struct list_head entry;
/*该kobject对象的上层结点*/
struct kobject *parent;
/*该kobject对象所属于的kset对象*/
struct kset *kset;
/*该kobject对象的sysfs文件系统的相关操作和属性,重要*/
struct kobj_type *ktype;
/*该kobject对象在sysfs文件系统中所对应的文件目录*/
struct kernfs_node *sd; /* sysfs directory entry */
/*该kobject对象的引用次数*/
struct kref kref;
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
struct delayed_work release;
#endif
/*该kobject对象的初始化状态*/
unsigned int state_initialized:1;
/*表示kobject对象是否在sysfs下建立目录*/
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
2.构建kobject对象
下面将从源码分析如何构建kobject对象以及kobject对象怎么关联sysf文件系统
2.1 kobject_create_and_add
在lib/kobject.c中有kobject_create_and_add函数,此函数为构建kobject对象的入口
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
{
struct kobject *kobj;
int retval;
kobj = kobject_create();
if (!kobj)
return NULL;
retval = kobject_add(kobj, parent, "%s", name);
if (retval) {
pr_warn("%s: kobject_add error: %d\n", __func__, retval);
kobject_put(kobj);
kobj = NULL;
}
return kobj;
}
其中kobject_create()
作用是创建并且初始化一个kobject对象,kobject_add(kobj, parent, "%s", name)
则是在sysfs目录下创建一个目录项与该kobject对象进行关联
2.2.1 kobject_create
struct kobject *kobject_create(void)
{
struct kobject *kobj;
kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
if (!kobj)
return NULL;
kobject_init(kobj, &dynamic_kobj_ktype);
return kobj;
}
static struct kobj_type dynamic_kobj_ktype = {
.release = dynamic_kobj_release,
.sysfs_ops = &kobj_sysfs_ops,
};
const struct sysfs_ops kobj_sysfs_ops = {
.show = kobj_attr_show,
.store = kobj_attr_store,
};
先动态分配内存,存放kobject对象,并调用kobject_init完成目录属性文件的操作接口设置,设置kobject对象的ktype为dynamic_kobj_ktype,设置dynamic_kobj_ktype中的sysfs_ops变量为kobj_sysfs_ops,设置kobj_sysfs_ops为show变量为kobj_attr_show,store变量为kobj_attr_store,这就是该kobject对象默认的属性文件操作接口,分别对应着读写操作
2.3 kobject_init
lib/kobject.c
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
。。。。
kobject_init_internal(kobj);
kobj->ktype = ktype;
return;
。。。。
}
kobj->ktype = ktype用来初始化目录属性文件操作接口
2.4 kobject_init_internal
lib/kobject.c
static void kobject_init_internal(struct kobject *kobj)
{
if (!kobj)
return;
kref_init(&kobj->kref);
INIT_LIST_HEAD(&kobj->entry);
kobj->state_in_sysfs = 0;
kobj->state_add_uevent_sent = 0;
kobj->state_remove_uevent_sent = 0;
kobj->state_initialized = 1;
}
kref_init(&kobj->kref)出初始化该kobject对象的引用次数,INIT_LIST_HEAD(&kobj->entry)初始化链表指针,kobj->state_in_sysfs = 0表示该kobject读写还没有与sysfs文件目录相关联,kobj->state_initialized = 1代表kobject对象已经初始化了
2.5 kobject_add
kobject_create函数大致已经分析完成,下面分析kobject_add函数,位于内核代码的lib/kobject.c下
,函数原型为:
int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...)
{
va_list args;
int retval;
...........
va_start(args, fmt);
retval = kobject_add_varg(kobj, parent, fmt, args);
va_end(args);
return retval;
}
struct kobject *kobj:表示传入的kobject对象,由前面kobject_create创建
struct kobject *parent:表示传入的kobject对象的parent指针
const char *fmt, ...:一堆变参
2.6 kobject_add_varg
static __printf(3, 0) int kobject_add_varg(struct kobject *kobj,
struct kobject *parent,
const char *fmt, va_list vargs)
{
int retval;
retval = kobject_set_name_vargs(kobj, fmt, vargs);
if (retval) {
pr_err("kobject: can not set name properly!\n");
return retval;
}
kobj->parent = parent;
return kobject_add_internal(kobj);
}
在这个函数中设置kObject对象的parent指针
2.7 kobject_set_name_vargs
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
va_list vargs)
{
const char *s;
if (kobj->name && !fmt)
return 0;
//参数格式化打印到s字符串中
s = kvasprintf_const(GFP_KERNEL, fmt, vargs);
if (!s)
return -ENOMEM;
/*
* ewww... some of these buggers have '/' in the name ... If
* that's the case, we need to make sure we have an actual
* allocated copy to modify, since kvasprintf_const may have
* returned something from .rodata.
*/
if (strchr(s, '/')) {
char *t;
t = kstrdup(s, GFP_KERNEL);
kfree_const(s);
if (!t)
return -ENOMEM;
strreplace(t, '/', '!');
s = t;
}
kfree_const(kobj->name);
//设置kobject对象的名字
kobj->name = s;
return 0;
}
kobject_set_name_vargs则是设置kObject对象的名字
2.8 kobject_add_internal
static int kobject_add_internal(struct kobject *kobj)
{
int error = 0;
struct kobject *parent;
......
parent = kobject_get(kobj->parent);
/* join kset if set, use it as parent if we do not already have one */
if (kobj->kset) {
if (!parent)
parent = kobject_get(&kobj->kset->kobj);
kobj_kset_join(kobj);
kobj->parent = parent;
}
pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
kobject_name(kobj), kobj, __func__,
parent ? kobject_name(parent) : "<NULL>",
kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
error = create_dir(kobj);
if (error) {
kobj_kset_leave(kobj);
kobject_put(parent);
kobj->parent = NULL;
/* be noisy on error issues */
if (error == -EEXIST)
pr_err("%s failed for %s with -EEXIST, don't try to register things with the same name in the same directory.\n",
__func__, kobject_name(kobj));
else
pr_err("%s failed for %s (error: %d parent: %s)\n",
__func__, kobject_name(kobj), error,
parent ? kobject_name(parent) : "'none'");
} else
kobj->state_in_sysfs = 1;
return error;
}
struct kobject *kobject_get(struct kobject *kobj)
{
if (kobj) {
if (!kobj->state_initialized)
WARN(1, KERN_WARNING
"kobject: '%s' (%p): is not initialized, yet kobject_get() is being called.\n",
kobject_name(kobj), kobj);
kref_get(&kobj->kref);
}
return kobj;
}
static inline void kref_get(struct kref *kref)
{
refcount_inc(&kref->refcount);
}
通过kobject_get(kobj->parent)来获取kobject对象的所属于的上一级kobject对象,如果kobject对象的所属于的上一级kobject对象存在,那么将上一级的kobject对象引用加1,如果kobject对象的kset指针存在,而kobject对象的parent指针不存在,那么将parent设置为kobj->kset->kobj,并通过kobj_kset_join将kObject对象放入放入kset链表的末尾,并设置kobject的parent指针,kobj->state_in_sysfs = 1标志着该kobject对象已经与sysfs文件系统关联起来
2.9 create_dir()
从字面意思可知为创建一个dir,即目录
static int create_dir(struct kobject *kobj)
{
const struct kobj_type *ktype = get_ktype(kobj);
const struct kobj_ns_type_operations *ops;
int error;
error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj));
if (error)
return error;
error = populate_dir(kobj);
if (error) {
sysfs_remove_dir(kobj);
return error;
}
if (ktype) {
error = sysfs_create_groups(kobj, ktype->default_groups);
if (error) {
sysfs_remove_dir(kobj);
return error;
}
}
/*
* @kobj->sd may be deleted by an ancestor going away. Hold an
* extra reference so that it stays until @kobj is gone.
*/
sysfs_get(kobj->sd);
/*
* If @kobj has ns_ops, its children need to be filtered based on
* their namespace tags. Enable namespace support on @kobj->sd.
*/
ops = kobj_child_ns_ops(kobj);
if (ops) {
BUG_ON(ops->type <= KOBJ_NS_TYPE_NONE);
BUG_ON(ops->type >= KOBJ_NS_TYPES);
BUG_ON(!kobj_ns_type_registered(ops->type));
sysfs_enable_ns(kobj->sd);
}
return 0;
}
其中sysfs_create_dir_ns为创建一个目录项,并关联父结点目录项,如果默认的属性存在,populate_dir用来创建默认的属性文件,即前面的kobj_sysfs_ops,sysfs_create_groups也用来创建属性文件接口
2.9 sysfs_create_dir_ns
fs/sysfs/dir.c
int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
{
struct kernfs_node *parent, *kn;
kuid_t uid;
kgid_t gid;
BUG_ON(!kobj);
if (kobj->parent)
/*获取上一层节点的目录项*/
parent = kobj->parent->sd;
else
/*设置上一层节点的目录项为sysfs根目录*/
parent = sysfs_root_kn;
if (!parent)
return -ENOENT;
kn = kernfs_create_dir_ns(parent, kobject_name(kobj),
S_IRWXU | S_IRUGO | S_IXUGO, uid, gid,
kobj, ns);
...
/*kobj对象关联sysfs目录项*/
kobj->sd = kn;
return 0;
}
在这个函数中先获取到上一层结点的目录项,如果上一层结点的目录项不存在的话,那么则设置上一层结点的目录项为sysfs根目录
2.10 kernfs_create_dir_ns
struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
const char *name, umode_t mode,
kuid_t uid, kgid_t gid,
void *priv, const void *ns)
{
struct kernfs_node *kn;
int rc;
/* allocate */
kn = kernfs_new_node(parent, name, mode | S_IFDIR,
uid, gid, KERNFS_DIR);
...
/*sysfs目录项关联kobject对象*/
kn->priv = priv;
...
}
其中S_IFDIR和KERNFS_DIR表明创建的是一个目录,
2.11 kernfs_new_node
struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
const char *name, umode_t mode,
kuid_t uid, kgid_t gid,
unsigned flags)
{
struct kernfs_node *kn;
kn = __kernfs_new_node(kernfs_root(parent),
name, mode, uid, gid, flags);
if (kn) {
kernfs_get(parent);
kn->parent = parent;
}
return kn;
}
__kernfs_new_node创建一个新的kernfs_node结点,并且设置其父指针为传进来的kernfs_node *parent,即sysfs_create_dir_ns中创建的父结点目录项
3.总结
以上,我们可以用下图表示