您的位置:首页 > 运维架构 > Linux

Linux设备驱动模型二 kobject

2015-12-03 14:40 621 查看

1 kobject

1.1 kobject数据结构

kobject是sysfs文件系统的基础数据结构,它定义在include/linux/kobjec.h中

struct kobject {
/*名称*/
const char     *name;
/*与与所属的kset(list成员)组成链表*/
struct list_head   entry;
/*父kobject;此成员未指定时,默认指向所属kset的kobject成员;在/sys文件系统中表示目录的上一层*/
struct kobject     *parent;
/*指向所属的kset,可为NULL*/
struct kset        *kset;
/*提供操作kobject属性特征(attribute)的接口*/
struct kobj_type   *ktype;
/*sys文件信息*/
struct sysfs_dirent    *sd;
/*kobject的引用计数*/
struct kref        kref;

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;
};


kset的定义如下:

struct kset {
/*与子kobject的entry成员组成链表*/
struct list_head list;
/*自旋锁*/
spinlock_t list_lock;
/*kobject*/
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
};


kobj_type的定义如下:

struct kobj_type {
/*释放函数*/
void (*release)(struct kobject *kobj);
/*sys文件操作函数*/
const struct sysfs_ops *sysfs_ops;
/*文件属性*/
struct attribute **default_attrs;
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
const void *(*namespace)(struct kobject *kobj);
};


sysfs_direct被定义在fs/sysfs/sysfs.h,它的定义如下:

struct sysfs_dirent {
atomic_t		s_count;
atomic_t		s_active;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map	dep_map;
#endif
/*上级目录*/
struct sysfs_dirent	*s_parent;
struct sysfs_dirent	*s_sibling;
/*名称*/
const char		*s_name;

const void		*s_ns; /* namespace tag */
union {
struct sysfs_elem_dir		s_dir;
struct sysfs_elem_symlink	s_symlink;
struct sysfs_elem_attr		s_attr;
struct sysfs_elem_bin_attr	s_bin_attr;
};

unsigned int		s_flags;
unsigned short		s_mode;
ino_t			s_ino;
struct sysfs_inode_attrs *s_iattr;
};



1.2 Kobject创建流程

我们看一下kobject的初始化过程。

初始化kobject有两种方式,分别是用kobject_init_and_addkobject_create_and_add函数,他们的区别是:

1)kobject_init_and_add传入一个kobject指针和kobj_type指针,然后进行初始化

2)kobject_create_and_add创建一个kobject变量,并返回其指针,它不用传入kobj_type指针

1.2.1 kobject_init_and_add

下面看kobject_init_and_add函数的实现:

/**
* kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy
* @kobj: pointer to the kobject to initialize
* @ktype: pointer to the ktype for this kobject.
* @parent: pointer to the parent of this kobject.
* @fmt: the name of the kobject.
*
* This function combines the call to kobject_init() and
* kobject_add().  The same type of error handling after a call to
* kobject_add() and kobject lifetime rules are the same here.
*/
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
struct kobject *parent, const char *fmt, ...)
{
va_list args;
int retval;

kobject_init(kobj, ktype);

va_start(args, fmt);
retval = kobject_add_varg(kobj, parent, fmt, args);
va_end(args);

return retval;
}

它首先调用了kobject_init函数,再调用kobject_add_varg函数。先看kobject_init函数的实现:

/**
* kobject_init - initialize a kobject structure
* @kobj: pointer to the kobject to initialize
* @ktype: pointer to the ktype for this kobject.
*
* This function will properly initialize a kobject such that it can then
* be passed to the kobject_add() call.
*
* After this function is called, the kobject MUST be cleaned up by a call
* to kobject_put(), not by a call to kfree directly to ensure that all of
* the memory is cleaned up properly.
*/
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
char *err_str;

if (!kobj) {
err_str = "invalid kobject pointer!";
goto error;
}
/*ktype不能为NULL*/
if (!ktype) {
err_str = "must have a ktype to be initialized properly!\n";
goto error;
}
if (kobj->state_initialized) {
/* do not error out as sometimes we can recover */
printk(KERN_ERR "kobject (%p): tried to init an initialized "
"object, something is seriously wrong.\n", kobj);
dump_stack();
}
/*调用kobject_init_internal函数*/
kobject_init_internal(kobj);
/*设置ktype*/
kobj->ktype = ktype;
return;

error:
printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
dump_stack();
}

从上面的代码可以看到,它调用了kobject_init_internal函数,并设置了kobject所指向的ktype,ktype必须不能为NULL。

再看kobject_init_internal函数的实现:

static void kobject_init_internal(struct kobject *kobj)
{
if (!kobj)
return;
/*初始化引用计数器为1*/
kref_init(&kobj->kref);
/*初始化entry链表结点,用于与所属的kset的list成员组成链表*/
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;
}

kobject_init_internal函数初始化引用计数器为1,并初始化entry链表结点。

我们再次回到kobject_init_and_add函数中,它接着调用了kobject_add_varg函数:

static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
const char *fmt, va_list vargs)
{
int retval;
/*设置kobject的名称*/
retval = kobject_set_name_vargs(kobj, fmt, vargs);
if (retval) {
printk(KERN_ERR "kobject: can not set name properly!\n");
return retval;
}
/*设置父kobject*/
kobj->parent = parent;
/*调用kobject_add_internal函数*/
return kobject_add_internal(kobj);
}

kobject_add_varg函数主要做了以下3个工作:

1)动过kobject_set_name_vargs设置kobject的名称

2)设置kobject的parent成员,即所指向的父kobject,可以为NULL

3)调用kobject_add_internal函数

kobject_add_internal函数比较关键,接着看它的实现:

static int kobject_add_internal(struct kobject *kobj)
{
int error = 0;
struct kobject *parent;

if (!kobj)
return -ENOENT;

if (!kobj->name || !kobj->name[0]) {
WARN(1, "kobject: (%p): attempted to be registered with empty "
"name!\n", kobj);
return -EINVAL;
}
/*获取parent指向的kobject,这里调用了kobject_get,结束的时候必须用kobject_put*/
parent = kobject_get(kobj->parent);

/* join kset if set, use it as parent if we do not already have one */
if (kobj->kset) {
/*如果parent没有设置,把parent指向所属ket的kobj成员*/
if (!parent)
parent = kobject_get(&kobj->kset->kobj);
/*把kobject的entry成员添加到kset的list链表中*/
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>");
/*创建sys目录*/
error = create_dir(kobj);
if (error) {
kobj_kset_leave(kobj);
kobject_put(parent);
kobj->parent = NULL;

/* be noisy on error issues */
if (error == -EEXIST)
printk(KERN_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
printk(KERN_ERR "%s failed for %s (%d)\n",
__func__, kobject_name(kobj), error);
dump_stack();
} else
kobj->state_in_sysfs = 1;

return error;
}

kobject_add_internal函数有几个关键点:

1)如果kobject的kset成员不为NULL,它会调用kobj_kset_join函数把kobject的entry成员添加到kset的list链表中

2)如果kobject的parent成员为NULL,则把它指向kset的kobject成员。

3)最后调用create_dir函数创建sys目录,关于create_dir,在上一章中有描述。

接着看kobj_kset_join函数的实现:

/* add the kobject to its kset's list */
static void kobj_kset_join(struct kobject *kobj)
{
if (!kobj->kset)
return;

kset_get(kobj->kset);
spin_lock(&kobj->kset->list_lock);
list_add_tail(&kobj->entry, &kobj->kset->list);
spin_unlock(&kobj->kset->list_lock);
}


很清楚地看到,此段代码把kobject的entry成员添加到以kset的list成员为头结点的链表中。

1.2.2 创建流程图



1.2.3 kobject_create_and_add

kobject_create_and_add的实现如下:

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) {
printk(KERN_WARNING "%s: kobject_add error: %d\n",
__func__, retval);
kobject_put(kobj);
kobj = NULL;
}
return kobj;
}

它调用了kobject_create和kobject_add函数。

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;

可以看到,和之前的kobject_init_and_add函数一样,它也调用了kobject_init函数来初始化kobject,但此时传入的kobj_type指针是系统定义的kobj_type变量:

static struct kobj_type dynamic_kobj_ktype = {
.release	= dynamic_kobj_release,
.sysfs_ops	= &kobj_sysfs_ops,
};

所以,我们应该明白:通过kobject_init_and_add初始化的kobject,其ktype成员是外部指定的,而通过kobject_create_and_add初始化的kobject,其ktype成员是系统定义的。

再看kobject_add的实现:

int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...)
{
va_list args;
int retval;

if (!kobj)
return -EINVAL;

if (!kobj->state_initialized) {
printk(KERN_ERR "kobject '%s' (%p): tried to add an "
"uninitialized object, something is seriously wrong.\n",
kobject_name(kobj), kobj);
dump_stack();
return -EINVAL;
}
va_start(args, fmt);
retval = kobject_add_varg(kobj, parent, fmt, args);
va_end(args);

return retval;
}

可以看到,它调用了kobject_add_varg函数,此函数的实现在之前已描述过。

1.2.4 创建流程图



1.2.5 代码示例1

文件kobject_demo1.c

#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sysfs.h>
#include <linux/kernel.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/string.h>

/*kobject变量*/
static struct kobject *mp_kobj;

/*
struct attribute {
const char		*name;
mode_t			mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lock_class_key	*key;
struct lock_class_key	skey;
#endif
};
*/

/*struct attribute变量*/
static struct attribute m_attr = {
.name = "name",
.mode = S_IRWXUGO,
};

/*struct attribute数组*/
static struct attribute *m_attrs[] = {
&m_attr,
NULL,/*末尾必须为NULL*/

};

/**********************************************************/
//sysfs_ops

/*
struct sysfs_ops {
ssize_t	(*show)(struct kobject *, struct attribute *,char *);
ssize_t	(*store)(struct kobject *,struct attribute *,const char *, size_t);
};
*/

/*sysfs_ops的show函数实现*/
static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
ssize_t count = 0;

printk("%s\n", __FUNCTION__);
count = sprintf(buf, "%s\n", kobject_name(kobj) );

return count;
}

/*sysfs_ops的store函数实现*/
static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
printk("%s\n", __FUNCTION__);
return 0;
}

/*struct sysfs_ops变量 */
static struct sysfs_ops m_sys_ops = {
.show 	= kobj_attr_show,
.store 	= kobj_attr_store,
};

/**********************************************************/
/*模块加载函数*/
static int __init kobj_init(void)
{
int error = 0;
/*struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
if use kobject_create_and_add, it will auto bind ktype to dynamic_kobj_ktype
so we can not set ktype member*/
mp_kobj = kobject_create_and_add("kobj_demo1_1", NULL);
if (!mp_kobj) {
goto out;
}

printk("%s success.\n", __FUNCTION__);

return 0;

out:
printk("%s failed!\n", __FUNCTION__);
return error;
}

/*模块退出函数*/
static void __exit kobj_exit(void)
{
//删除kobject
kobject_del(mp_kobj);
//使引用计数减1并调用kobj_type的release函数
kobject_put(mp_kobj);
printk("%s\n", __FUNCTION__);
}

module_init(kobj_init);
module_exit(kobj_exit);

MODULE_AUTHOR("tonny");
MODULE_DESCRIPTION("kobject demo");
MODULE_LICENSE("GPL");


文件Makefile

FILE=kobject_demo1
obj-m:=$(FILE).o
KERNELBUILD :=/lib/modules/$(shell uname -r)/build
default:
make -C $(KERNELBUILD) M=$(shell pwd) modules
echo insmod/rmmod ./$(FILE).ko to load or uninstall
clean:
rm -rf *.o *.ko *.mod.c .*.cmd *.markers *.order *.symvers .tmp_versions


编译及运行:

$ make					#编译模块
$ sudo dmesg -c            		#清除内核日志
$ sudo insmod ./kobject_demo1.ko   	#加载内核模块
$ sudo dmesg                		#察看内核日志
[ 1954.839828] kobj_init success.

$ cd /sys/kobj_demo1/           		#进入/sys目录
$ pwd
/sys/kobj_demo1
$ ls
name
$ cat name              			#察看kobject属性
kobj_demo1
$ sudo rmmod ./kobject_demo1.ko        #卸载内核模块
$ sudo dmesg
[ 3609.041498] kobj_release
[ 3609.041501] kobj_exit


kobject_demo1.c的关键地方:

1)通过kobject_init_and_add函数初始化kobject,同时传入了ktype变量。

2)因为没有设置父kobject或kset,所以kobject_demo1出现在/sys顶层目录下

3)在删除kobject的时候,必须调用kobject_put函数,以便清除kobject所有相关的内存,kobject_put函数的实现如下:

/**
* kobject_put - decrement refcount for object.
* @kobj: object.
*
* Decrement the refcount, and if 0, call kobject_cleanup().
*/
void kobject_put(struct kobject *kobj)
{
if (kobj) {
if (!kobj->state_initialized)
WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "
"initialized, yet kobject_put() is being "
"called.\n", kobject_name(kobj), kobj);
kref_put(&kobj->kref, kobject_release);
}
}

1.2.6 代码示例2

通过kobject_create_and_add函数创建kobject

文件kobject_demo1_1.c

#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sysfs.h>
#include <linux/kernel.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/string.h>

/*kobject变量*/
static struct kobject *mp_kobj;

/*
struct attribute {
const char		*name;
mode_t			mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lock_class_key	*key;
struct lock_class_key	skey;
#endif
};
*/

/*struct attribute变量*/
static struct attribute m_attr = {
.name = "name",
.mode = S_IRWXUGO,
};

/*struct attribute数组*/
static struct attribute *m_attrs[] = {
&m_attr,
NULL,/*末尾必须为NULL*/

};

/**********************************************************/
//sysfs_ops

/*
struct sysfs_ops {
ssize_t	(*show)(struct kobject *, struct attribute *,char *);
ssize_t	(*store)(struct kobject *,struct attribute *,const char *, size_t);
};
*/

/*sysfs_ops的show函数实现*/
static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
ssize_t count = 0;

printk("%s\n", __FUNCTION__);
count = sprintf(buf, "%s\n", kobject_name(kobj) );

return count;
}

/*sysfs_ops的store函数实现*/
static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
printk("%s\n", __FUNCTION__);
return 0;
}

/*struct sysfs_ops变量 */
static struct sysfs_ops m_sys_ops = {
.show 	= kobj_attr_show,
.store 	= kobj_attr_store,
};

/**********************************************************/
/*模块加载函数*/
static int __init kobj_init(void)
{
int error = 0;
/*struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
if use kobject_create_and_add, it will auto bind ktype to dynamic_kobj_ktype
so we can not set ktype member*/
mp_kobj = kobject_create_and_add("kobj_demo1_1", NULL);
if (!mp_kobj) {
goto out;
}

printk("%s success.\n", __FUNCTION__);

return 0;

out:
printk("%s failed!\n", __FUNCTION__);
return error;
}

/*模块退出函数*/
static void __exit kobj_exit(void)
{
//删除kobject
kobject_del(mp_kobj);
//使引用计数减1并调用kobj_type的release函数
kobject_put(mp_kobj);
printk("%s\n", __FUNCTION__);
}

module_init(kobj_init);
module_exit(kobj_exit);

MODULE_AUTHOR("tonny");
MODULE_DESCRIPTION("kobject demo");
MODULE_LICENSE("GPL");

与kobject_demo1.c不同的是,kobject_demo1_1.c并没有创建kobj_type变量

1.2.7 代码示例3

前面两个示例并没有指定父kobject即parent成员,下面指定其parent成员

kobject_demo2.c

#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sysfs.h>
#include <linux/kernel.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/string.h>

#define ATTR_NAME "name"
#define ATTR_VALUE	"value"

struct my_object {
struct kobject kobj;
int value;
};

static struct my_object *m_obj1;
static struct my_object *m_obj2;

/**********************************************************/
//attrubute

static struct attribute m_attr_name = {
.name = ATTR_NAME,
.mode = S_IRWXUGO,
};

static struct attribute m_attr_value = {
.name = ATTR_VALUE,
.mode = S_IRWXUGO,
};

static struct attribute *m_attrs[] = {
&m_attr_name,
&m_attr_value,
NULL,

};

/**********************************************************/
//sysfs_ops

static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
ssize_t count = 0;
struct my_object *mobj;

printk("%s\n", __FUNCTION__);

if (!strcmp(attr->name, ATTR_NAME)) {
count = sprintf(buf, "%s\n", kobject_name(kobj) );
} else if (!strcmp(attr->name, ATTR_VALUE)) {
mobj = container_of(kobj, struct my_object, kobj);
count = sprintf(buf, "%d\n", mobj->value);
}

return count;
}

static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct my_object *mobj;

printk("%s\n", __FUNCTION__);

if (!strcmp(attr->name, ATTR_VALUE)) {
mobj = container_of(kobj, struct my_object, kobj);
sscanf(buf, "%d", &mobj->value);
}

return count;
}

static struct sysfs_ops m_sys_ops = {
.show 	= kobj_attr_show,
.store 	= kobj_attr_store,
};

/**********************************************************/
//kobj_type

void kobj_release(struct kobject *kobj)
{
struct my_object *mobj = container_of(kobj, struct my_object, kobj);
if (mobj) {
//释放my_object内存
kfree(mobj);
}
printk("%s\n", __FUNCTION__);

}

static struct kobj_type m_ktype = {
.release = kobj_release,
.sysfs_ops = &m_sys_ops,
.default_attrs = m_attrs,
};

/**********************************************************/
static int __init kobj_init(void)
{
int error = 0;
//申请m_obj1内存
m_obj1 = (struct my_object *)kzalloc(sizeof(struct my_object), GFP_KERNEL);

if (!m_obj1) {
error = -ENOMEM;
goto out;
}
//申请m_obj2内存
m_obj2 = (struct my_object *)kzalloc(sizeof(struct my_object), GFP_KERNEL);

if (!m_obj2) {
error = -ENOMEM;
goto out1;
}

//初始化m_obj1的kobject
error = kobject_init_and_add(&m_obj1->kobj, &m_ktype, NULL, "kobj_demo1");
if (error) {
goto out2;
}

////初始化m_obj2的kobject,并指定其parent为m_obj1->kobj
error = kobject_init_and_add(&m_obj2->kobj, &m_ktype, &m_obj1->kobj, "kobj_demo2");
if (error) {
goto out2;
}
//初始化value值
m_obj1->value = 1;
m_obj2->value = 2;

printk("%s success.\n", __FUNCTION__);

return 0;
out2:
kfree(m_obj2);
out1:
kfree(m_obj1);
out:
printk("%s failed!\n", __FUNCTION__);
return error;
}

static void __exit kobj_exit(void)
{
//删除kobject
kobject_del(&m_obj2->kobj);
//使引用计数减1并调用kobj_type的release函数
kobject_put(&m_obj2->kobj);

kobject_del(&m_obj1->kobj);
kobject_put(&m_obj1->kobj);

printk("%s\n", __FUNCTION__);
}

module_init(kobj_init);
module_exit(kobj_exit);

MODULE_AUTHOR("tonny");
MODULE_DESCRIPTION("kobject demo");
MODULE_LICENSE("GPL");
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: