1、kobject 结构
在Linux内核里,kobject是组成Linux设备模型的基础,一个kobject对应sysfs里的一个目录。从面向对象的角度来说,kobject可以看作是所有设备对象的基类,因为C语言并没有面向对象的语法,所以一般是把kobject内嵌到其他结构体里来实现类似的作用,这里的其他结构体可以看作是kobject的派生类。Kobject为Linux设备模型提供了很多有用的功能,比如引用计数,接口抽象,父子关系等等。引用计数本质上就是利用kref实现的。
另外,Linux设备模型还有一个重要的数据结构kset。Kset本身也是一个kobject,所以它在sysfs里同样表现为一个目录,但它和kobject的不同之处在于kset可以看作是一个容器,如果你把它类比为C++里的容器类如list也无不可。Kset之所以能作为容器来使用,其内部正是内嵌了一个双向链表结构struct list_head。
kobject 在内核中的描述
- struct kobject {
- const char *name;
- struct list_head entry;
- struct kobject *parent;
- struct kset *kset;
- struct kobj_type *ktype;
- struct sysfs_dirent *sd;
- 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;
- };
内核里的设备之间是以树状形式组织的,在这种组织架构里比较靠上层的节点可以看作是下层节点的父节点,反映到sysfs里就是上级目录和下级目录之间的关系, 在内核里,正是kobject帮助我们实现这种父子关系 。在kobject的定义里,name表示的是kobject在sysfs中的名字;指针parent用来指向kobject的父对象;Kref大家应该比较熟悉了,kobject通过它来实现引用计数;Kset指针用来指向这个kobject所属的kset,下文会再详细描述kset的用法;对于ktype,如果只是望文生义的话,应该是用来描述kobject的类型信息。Ktype的定义如下:
- struct kobj_type {
- void (*release)(struct kobject *kobj);
- const struct sysfs_ops *sysfs_ops;
- struct attribute **default_attrs;
- };
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/kobject.h>
- struct my_kobj { //内嵌kobject的结构
- int val;
- struct kobject kobj;
- };
- struct my_kobj *obj1, *obj2;
- struct kobj_type my_type;
- struct attribute name_attr = {
- .name = "name", //文件名
- .mode = 0444, //指定文件的访问权限
- };
- struct attribute val_attr = {
- .name = "val", //文件名
- .mode = 0666, //指定文件的访问权限
- };
- struct attribute *my_attrs[] = {
- &name_attr,
- &val_attr,
- NULL,
- };
- /*
- 结构体struct attribute里的name变量用来指定文件名,mode变量用来指定文件的访问权限。
- 这里需要着重指出的是,数组my_attrs的最后一项一定要赋为NULL,否则会造成内核oops。
- */
- ssize_t my_show(struct kobject *kobj, struct attribute *attr, char *buffer)
- {
- struct my_kobj *obj = container_of(kobj, struct my_kobj, kobj);
- ssize_t count = 0;
- if (strcmp(attr->name, "name") == 0) {
- count = sprintf(buffer, "%s\n", kobject_name(kobj));
- } else if (strcmp(attr->name, "val") == 0) {
- count = sprintf(buffer, "%d\n", obj->val);
- }
- return count;
- }
- ssize_t my_store(struct kobject *kobj, struct attribute *attr, const char *buffer, size_t size)
- {
- struct my_kobj *obj = container_of(kobj, struct my_kobj, kobj);
- if (strcmp(attr->name, "val") == 0) {
- sscanf(buffer, "%d", &obj->val);
- }
- return size;
- }
- struct sysfs_ops my_sysfsops = {
- .show = my_show,
- .store = my_store,
- };
- void obj_release(struct kobject *kobj)
- {
- struct my_kobj *obj = container_of(kobj, struct my_kobj, kobj);
- printk(KERN_INFO "obj_release %s\n", kobject_name(&obj->kobj));
- kfree(obj);
- }
- static int __init mykobj_init(void)
- {
- printk(KERN_INFO "mykobj_init\n");
- obj1 = kzalloc(sizeof(struct my_kobj), GFP_KERNEL); //分配obj1和obj2并赋值
- if (!obj1) {
- return -ENOMEM;
- }
- obj1->val = 1;
- obj2 = kzalloc(sizeof(struct my_kobj), GFP_KERNEL);
- if (!obj2) {
- kfree(obj1);
- return -ENOMEM;
- }
- obj2->val = 2;
- my_type.release = obj_release;
- my_type.default_attrs = my_attrs;
- my_type.sysfs_ops = &my_sysfsops;
- kobject_init_and_add(&obj1->kobj, &my_type, NULL, "mykobj1"); /*函数来初始化kobject并把它加入到设备模型的体系架构*/
- kobject_init_and_add(&obj2->kobj, &my_type, &obj1->kobj, "mykobj2");
- /*
- kobject_init用来初始化kobject结构,kobject_add用来把kobj加入到设备模型之中。
- 在实作中,我们先对obj1进行初始化和添加的动作,调用参数里,parent被赋为NULL,表示obj1没有父对象,反映到sysfs里,
- my_kobj1的目录会出现在/sys下,obj2的父对象设定为obj1,那么my_kobj2的目录会出现在/sys/my_kobj1下面。
- 前面提到,kobject也提供了引用计数的功能,虽然本质上是利用kref,但也提供了另外的接口供用户使用。
- kobject_init_and_add和kobject_init这两个函数被调用后,kobj的引用计数会初始化为1,
- 所以在module_exit时要记得用kobject_put来释放引用计数。
- */
- return 0;
- }
- static void __exit mykobj_exit(void)
- {
- printk(KERN_INFO "mykobj_exit\n");
- kobject_del(&obj2->kobj); /*先子对象,后父对象*/
- kobject_put(&obj2->kobj);
- kobject_del(&obj1->kobj);
- kobject_put(&obj1->kobj);
- return;
- }
- /*
- kobject_del的作用是把kobject从设备模型的那棵树里摘掉,同时sysfs里相应的目录也会删除。
- 这里需要指出的是,释放的顺序应该是先子对象,后父对象。
- 因为kobject_init_and_add和kobject_add这两个函数会调用kobject_get来增加父对象的引用计数,
- 所以kobject_del需要调用kobject_put来减少父对象的引用计数。在本例中,如果先通过kobject_put来释放obj1,
- 那kobject_del(&obj2->kobj)就会出现内存错误。
- */
- module_init(mykobj_init);
- module_exit(mykobj_exit);
- MODULE_LICENSE("GPL");