Linux设备驱动模型三 kset

1 kset数据结构

kset的定义在前文已有描述,我们再回顾一下:

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

2 kset创建流程


2.1 kset_create_and_add

kset的创建函数为kset_create_and_add,详细如下:

/**
 * kset_create_and_add - create a struct kset dynamically and add it to sysfs
 *
 * @name: the name for the kset
 * @uevent_ops: a struct kset_uevent_ops for the kset
 * @parent_kobj: the parent kobject of this kset, if any.
 *
 * This function creates a kset structure dynamically and registers it
 * with sysfs.  When you are finished with this structure, call
 * kset_unregister() and the structure will be dynamically freed when it
 * is no longer being used.
 *
 * If the kset was not able to be created, NULL will be returned.
 */
struct kset *kset_create_and_add(const char *name,
                                 const struct kset_uevent_ops *uevent_ops,
                                 struct kobject *parent_kobj)
{
    struct kset *kset;
    int error;
    /*创建kset对象*/
    kset = kset_create(name, uevent_ops, parent_kobj);
    if (!kset)
        return NULL;
    /*注册kset*/
    error = kset_register(kset);
    if (error) {
        kfree(kset);
        return NULL;
    }
    return kset;
} 

它调用了kset_createkset_register函数。

下面看kset_create的实现:

/**
 * kset_create - create a struct kset dynamically
 *
 * @name: the name for the kset
 * @uevent_ops: a struct kset_uevent_ops for the kset
 * @parent_kobj: the parent kobject of this kset, if any.
 *
 * This function creates a kset structure dynamically.  This structure can
 * then be registered with the system and show up in sysfs with a call to
 * kset_register().  When you are finished with this structure, if
 * kset_register() has been called, call kset_unregister() and the
 * structure will be dynamically freed when it is no longer being used.
 *
 * If the kset was not able to be created, NULL will be returned.
 */
static struct kset *kset_create(const char *name,
                                const struct kset_uevent_ops *uevent_ops,
                                struct kobject *parent_kobj)
{
    struct kset *kset;
    int retval;
    /*申请空间*/
    kset = kzalloc(sizeof(*kset), GFP_KERNEL);
    if (!kset)
        return NULL;
    /*设置kset的kobject名称*/
    retval = kobject_set_name(&kset->kobj, name);
    if (retval) {
        kfree(kset);
        return NULL;
    }
    kset->uevent_ops = uevent_ops;
    /*设置kobject的parent*/
    kset->kobj.parent = parent_kobj;

    /*
     * The kobject of this kset will have a type of kset_ktype and belong to
     * no kset itself.  That way we can properly free it when it is
     * finished being used.
     */
    /*设置kobject的ktype,为系统定义好的ktype*/
    kset->kobj.ktype = &kset_ktype;
    /*设置kobject的kset为NULL*/
    kset->kobj.kset = NULL;

    return kset;
} 

kset_create函数主要对ketkobj成员进行初始化和设置。按面向对象来说,kset可以视为kobject的子类:

1)调用kobject_set_name函数设置kobject的名称

2)设置kobjectparent为传入的形参parent_kobj

3)设置kobjectktype为系统定义好的ktype变量

4)设置kobject的所属ksetNULL,意思是kobject所属的kset就是kset本身,因为kset结构体包含了kobject成员。

 

kset_register的实现如下:

/**
 * kset_register - initialize and add a kset.
 * @k: kset.
 */
int kset_register(struct kset *k)
{
    int err;

    if (!k)
        return -EINVAL;
    /*初始化kset的kobject,和list链表*/
    kset_init(k);
    /*初始化kobject,创建对应的sys目录*/
    err = kobject_add_internal(&k->kobj);
    if (err)
        return err;
    kobject_uevent(&k->kobj, KOBJ_ADD);
    return 0;
} 

kset_register的关键内容:

1)调用kset_init函数,它的实现如下:

/**
 * kset_init - initialize a kset for use
 * @k: kset
 */
void kset_init(struct kset *k)
{
    /*初始化kobject*/
    kobject_init_internal(&k->kobj);
    /*初始化以list成员为头结点的链表,它和子kobject的entry成员组成链表*/
    INIT_LIST_HEAD(&k->list);
    spin_lock_init(&k->list_lock);
} 

可以看到,kset_init调用了kobject_init_internal来初始化kobjectkobject_init_internal在【kobject创建流程】中我们已有描述。

1)调用kobject_add_internal初始化kobject,此函数前文已有描述,主要创建kobjectsys目录。

2)需要注意的是,删除kset的时候,必须调用kset_unregister函数释放相应的空间。

2.2 代码示例1

  • 文件kset_demo1.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*kset变量*/
static struct kset *m_kset;

/**********************************************************/

/*模块加载函数*/
static int __init kset_module_init(void)
{
    m_kset = kset_create_and_add("kset_demo1", NULL, NULL);

    if (!m_kset) {
        goto out;
    }

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

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

/*模块退出函数*/
static void __exit kset_module_exit(void)
{
    //删除set
    kset_unregister(m_kset);
    printk("%s\n", __FUNCTION__);
}


module_init(kset_module_init);
module_exit(kset_module_exit);

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

编译并运行:

# insmod kset_demo1.ko
# dmesg
[1056355.652305] kset_module_init success.
# ls /sys/
block  bus  class  dev  devices  firmware  fs  hypervisor  kernel  kset_demo1  module  power
# rmmod kset_demo1.ko
# dmesg
[1056355.652305] kset_module_init success.
[1056382.292335] kset_module_exit 

可以看到,加载kset_demo1.ko后,在/sys目录下多了一个kset_demo1目录。


2.2 代码示例2

我们再创建一个kobject,把它挂到kset_demo下面。

文件kset_demo2.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*kset变量*/
static struct kset *m_kset;
/*kobject变量*/
static struct kobject m_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,
};

/*attribute数组*/
static struct attribute *m_attrs[] = {
	&m_attr,
	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,
};

/**********************************************************/
//kobj_type
/*kobj_type的release函数实现*/
void kobj_release(struct kobject *kobj)
{
	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;
	//创建kset
	m_kset = kset_create_and_add("kset_demo2", NULL, NULL);
	if(!m_kset){
		goto out;
	}

	//创建kobject,并把它的父kobject指向kset的kobject成员
	error = kobject_init_and_add(&m_kobj, &m_ktype, &m_kset->kobj, "kobj_demo1");
	if(error){
		//初始化失败后必须调用kobject_put清理相关内容
		goto out1;
	}

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

	return 0;

out1:
	//使引用计数减1并调用kobj_type的release函数
	kobject_put(&m_kobj);
out:
	printk("%s failed!\n", __FUNCTION__);
	return error;
}

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

	//删除kset
	kset_unregister(m_kset);
	printk("%s\n", __FUNCTION__);
}


module_init(kobj_init);
module_exit(kobj_exit);

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

文件Makefile
FILE=kset_demo2
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


编译并运行:

insmod kset_demo2.ko 
# dmesg
[2081793.060586] kobj_init success.
# ls /sys/kset_demo2/
kobj_demo1
# cat /sys/kset_demo2/kobj_demo1/name 
kobj_demo1
# rmmod kset_demo2.ko
# dmesg
[2081793.060586] kobj_init success.
[2081822.043931] kobj_attr_show
[2081838.532606] kobj_release
[2081838.532616] kobj_exit

可以看到,在kset_demo2下面,多了一个kobj_demo1目录。

3 总结

sysfs的目录层次图:

Linux设备驱动模型三 kset_第1张图片

我们再来回顾/sys的目录层次,

  • 在 /sys 根目录之下的都是 kset,它们组织了 /sys 的顶层目录视图;
  • 在部分 kset 下有二级或更深层次的 kset
  • 每个 kset 目录下再包含着一个或多个 kobject,这表示一个集合所包含的 kobject 结构体;
  • 在 kobject 下有属性(attrs)文件和属性组(attr_group),属性组就是组织属性的一个目录,它们一起向用户层提供了表示和操作这个 kobject 的属性特征的接口
  • 在 kobject 下还有一些符号链接文件,指向其它的 kobject,这些符号链接文件用于组织上面所说的 device, driver, bus_type, class, module 之间的关系;
  • 不同类型如设备类型的、设备驱动类型的 kobject 都有不同的属性,不同驱动程序支持
参考文章:使用 /sys 文件系统访问 Linux 内核


你可能感兴趣的:(Linux驱动)