Linux设备管理模型-01:基础数据结构

文章目录

  • 1. 设备管理模型
  • 2. 基本数据结构
    • 2.1 kobject
    • 2.2 kset

1. 设备管理模型

设备模型是内核提供的一个编写驱动的架构。
设备管理是设备-总线-驱动结构。
linux中的设备是由树状模型组织的,从sysfs中可以查看树状结构。

他本身实现了:

  • 电源管理
  • 热插拔(hotplug)事件管理

2. 基本数据结构

  • kobject, kset, uevent
  • device, device_driver, bus, class

2.1 kobject

许多内核对象都是由kobject作为基类派生的。相同类型的kobject通过其内嵌的list_head链成一个链表,然后使用另外一个结构体kset(kobject的子类)来指向和管理这个列表。

struct kobject {
	const char		*name;
	struct list_head	entry;
	struct kobject		*parent;
	struct kset		*kset; 		/* 给相关的kobject分组,是kobj的集合 */
	struct kobj_type	*ktype; /* 抽象出了一些kobj和sysfs的通用接口,描述kobj的行为 */
	struct kernfs_node	*sd; 	/* sysfs 的文件节点 */
	struct kref		kref; 		/* kobject的引用计数 */
#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要加入到设备管理模型中,需要使用函数:

/* 创建新的kobj */
struct kobject * __must_check kobject_create_and_add(const char *name,
						struct kobject *parent); 
/* parent : 在sysfs中的父目录,如果为NULL则表示在"/sys/"目录下 */

/* 添加已存在的的kobj */						
int kobject_init_and_add(struct kobject *kobj,
			 struct kobj_type *ktype, 
			 struct kobject *parent, 
			 const char *fmt, ...); 
/* fmt一般是name, 也是sysfs中kobject对应的目录名 */ 

调用之前需要给kobj分配内存,并初始化ktype,用于这个kset中kobject的通用操作,其中ktype如下:

struct kobj_type {
	void (*release)(struct kobject *kobj); 	/* 必须 */
	const struct sysfs_ops *sysfs_ops; 		/* 内含show和store接口 */
	struct attribute **default_attrs; 		/* 每个属性都在sysfs中有对应的属性文件 */
	const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
	const void *(*namespace)(struct kobject *kobj);
};

2.2 kset

kset用于:

  • 作为一组kobject的容器
  • 是一个sysfs中的目录,里面包含kset关联的kobject
  • kset可支持kobject的“热插拔”,并影响uevent事件像用户空间的报告方式
/**
 * struct kset - 一个子系统中,一系列kobject的集合
 *
 * A kset defines a group of kobjects.  They can be individually
 * different "types" but overall these kobjects all want to be grouped
 * together and operated on in the same manner.  ksets are used to
 * define the attribute callbacks and other common events that happen to
 * a kobject.
 *
 * @list: the list of all kobjects for this kset
 * @list_lock: a lock for iterating over the kobjects
 * @kobj: the embedded kobject for this kset (recursion, isn't it fun...)
 * @uevent_ops: the set of uevent operations for this kset.  These are
 * called whenever a kobject has something happen to it so that the kset
 * can add new environment variables, or filter out the uevents if so
 * desired.
 */
struct kset {
	struct list_head list;
	spinlock_t list_lock;
	struct kobject kobj;
	const struct kset_uevent_ops *uevent_ops;
} __randomize_layout;

一个组织多个kobject的kset例程(节选):linux/samples/kobject/kset-example.c

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

MODULE_LICENSE("GPL");

struct foo_obj {
	struct kobject kobj;
	int foo;
	int baz;
	int bar;
};
/* 从类型为kobj的结构体成员的指针x,获取该foo_obj类型结构体的指针 */
#define to_foo_obj(x) container_of(x, struct foo_obj, kobj)

static void foo_release(struct kobject *kobj)
{
	struct foo_obj *foo;

	foo = to_foo_obj(kobj);
	kfree(foo);
}

/* 可以定义所属kset的kobject的一些行为到default_attrs和sysfs_ops属性中,现在仅
 * 使用release */
static struct kobj_type foo_ktype = {
    // .sysfs_ops = &foo_sysfs_ops,
    .release = foo_release,
    // .default_attrs = foo_default_attrs,
};

static struct kset *example_kset;
static struct foo_obj *foo_obj;
static struct foo_obj *bar_obj;
static struct foo_obj *baz_obj;
/* 创建kset下的kobject */
static struct foo_obj *create_foo_obj(const char *name)
{
	struct foo_obj *foo;
	int retval;

	/* allocate the memory for the whole object */
	foo = kzalloc(sizeof(*foo), GFP_KERNEL);
	if (!foo)
		return NULL;

	/* 初始化kobject之前先确定所属kset */
	foo->kobj.kset = example_kset;

	/* 初始化kobject添加到kernel中,并关联ktype,会在sysfs中创建名为name的kobject文件夹
	 * 第三个参数是父kobj,由于已确定kset,写为NULL */
	retval = kobject_init_and_add(&foo->kobj, &foo_ktype, NULL, "%s", name);
	if (retval) {
		kobject_put(&foo->kobj);
		return NULL;
	}
	/* 通知用户空间有一个新的内核对象(kobject)已经被添加到 sysfs 中。
	 * 这对于用户空间的监控和管理工具来说是很有用的 */
	kobject_uevent(&foo->kobj, KOBJ_ADD);

	return foo;
}

static void destroy_foo_obj(struct foo_obj *foo)
{
	kobject_put(&foo->kobj);
}

static int __init example_init(void)
{
    /* 创建一个名为 "kset_example" 的kset, 路径在/sys/kernel/ */
    example_kset = kset_create_and_add("kset_example", NULL, kernel_kobj);
    if (!example_kset)
    	return -ENOMEM;
    /* 在已定义的kset下新增kobject */
    foo_obj = create_foo_obj("foo");
    if (!foo_obj)
    	goto foo_error;
    
    bar_obj = create_foo_obj("bar");
    if (!bar_obj)
    	goto bar_error;
    
    baz_obj = create_foo_obj("baz");
    if (!baz_obj)
    	goto baz_error;
    
    return 0;

baz_error:
	destroy_foo_obj(bar_obj);
bar_error:
	destroy_foo_obj(foo_obj);
foo_error:
	kset_unregister(example_kset);
	return -EINVAL;
}

static void __exit example_exit(void)
{
	destroy_foo_obj(baz_obj);
	destroy_foo_obj(bar_obj);
	destroy_foo_obj(foo_obj);
	kset_unregister(example_kset);
}

MODULE_AUTHOR("LUKEKE");        // 作者
MODULE_DESCRIPTION("kset test"); // 描述
MODULE_ALIAS("kset Learn");   // 别名

module_init(example_init);
module_exit(example_exit);

运行结果:

[root@qemu_imx6ul:/sys/kernel]# ls
config         irq            rcu_expedited  slab
debug          mm             rcu_normal     uevent_helper
fscaps         notes          security       uevent_seqnum
[root@qemu_imx6ul:/sys/kernel]# insmod ~/hello.ko 
[root@qemu_imx6ul:/sys/kernel]# ls
config         irq            notes          security       uevent_seqnum
debug          kset_example   rcu_expedited  slab
fscaps         mm             rcu_normal     uevent_helper
[root@qemu_imx6ul:/sys/kernel]# cd kset_example/
[root@qemu_imx6ul:/sys/kernel/kset_example]# ls
bar  baz  foo
[root@qemu_imx6ul:/sys/kernel/kset_example]# ls bar/
[root@qemu_imx6ul:/sys/kernel/kset_example]# ls foo

下一篇文:sysfs

你可能感兴趣的:(C,linux,数据结构,嵌入式)