首页 > 技术文章 > linux设备驱动(9)attribute详解

xinghuo123 2020-05-18 22:17 原文

1. 前言

sysfs是一个基于RAM的文件系统,它和kobject一起,可以将kernel的数据结构导出到用户空间,以文件目录结构的形式,提供对这些数据结构(以及数据结构的属性)的访问支持。

sysfs具备文件系统的所有属性,而本文主要侧重其设备模型的特性,因此不会涉及过多的文件系统实现细节。

2. attribute

2.1 attribute的功能概述

在sysfs中,为什么会有attribute的概念呢?其实它是对应kobject而言的,指的是kobject的“属性”。我们知道,

sysfs中的目录描述了kobject,而kobject是特定数据类型变量(如struct device)的体现。因此kobject的属性,就是这些变量的属性。它可以是任何东西,名称、一个内部变量、一个字符串等等。而attribute,在sysfs文件系统中是以文件的形式提供的,即:kobject的所有属性,都在它对应的sysfs目录下以文件的形式呈现。这些文件一般是可读、写的,而kernel中定义了这些属性的模块,会根据用户空间的读写操作,记录和返回这些attribute的值。

总结一下:所谓的attibute,就是内核空间和用户空间进行信息交互的一种方法。例如某个driver定义了一个变量,却希望用户空间程序可以修改该变量,以控制driver的运行行为,那么就可以将该变量以sysfs attribute的形式开放出来。

Linux内核中,attribute分为普通的attribute和二进制attribute,位于:include\linux\sysfs.h

详细如下:

 1 struct attribute {
 2     const char        *name;
 3     umode_t            mode;
 4 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 5     bool            ignore_lockdep:1;
 6     struct lock_class_key    *key;
 7     struct lock_class_key    skey;
 8 #endif
 9 }
10 
11 struct bin_attribute {
12     struct attribute    attr;
13     size_t            size;
14     void            *private;
15     ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *,
16             char *, loff_t, size_t);
17     ssize_t (*write)(struct file *,struct kobject *, struct bin_attribute *,
18              char *, loff_t, size_t);
19     int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr,
20             struct vm_area_struct *vma);
21 };

struct attribute为普通的attribute,使用该attribute生成的sysfs文件,只能用字符串的形式读写(后面会说为什么)。而struct bin_attribute在struct attribute的基础上,增加了read、write等函数,因此它所生成的sysfs文件可以用任何方式读写。

说完基本概念,我们要问两个问题:

kernel怎么把attribute变成sysfs中的文件呢?

用户空间对sysfs的文件进行的读写操作,怎么传递给kernel呢?

下面来看看这个过程。

2.2 attibute文件的创建

在linux内核中,attibute文件的创建是由fs/sysfs/file.c中sysfs_create_file接口完成的,该接口的实现没有什么特殊之处,大多是文件系统相关的操作,和设备模型关系不大,详见:Linux设备驱动(7)sysfs详解

2.3 attibute文件的read和write

看到2.1章节struct attribute的原型时,也许我们会犯嘀咕,该结构很简单啊,name表示文件名称,mode表示文件模式,其它的字段都是内核用于debug Kernel Lock的,那文件操作的接口在哪里呢?

不着急,我们去fs/sysfs目录下看看sysfs相关的代码逻辑。

所有的文件系统,都会定义一个struct file_operations变量,用于描述本文件系统的操作接口,sysfs也不例外:

位于:fs\sysfs\file.c

1 const struct file_operations sysfs_file_operations = {
2     .read        = sysfs_read_file,
3     .write        = sysfs_write_file,
4     .llseek        = generic_file_llseek,
5     .open        = sysfs_open_file,
6     .release    = sysfs_release,
7     .poll        = sysfs_poll,
8 };

attribute文件的read操作,会由VFS转到sysfs_file_operations的read(也就是sysfs_read_file)接口上,让我们大概看一下该接口的处理逻辑。

 1 struct sysfs_open_dirent {
 2     atomic_t        refcnt;
 3     atomic_t        event;
 4     wait_queue_head_t    poll;
 5     struct list_head    buffers; /* goes through sysfs_buffer.list */
 6 };
 7 
 8 struct sysfs_buffer {
 9     size_t            count;
10     loff_t            pos;
11     char            * page;
12     const struct sysfs_ops    * ops;
13     struct mutex        mutex;
14     int            needs_read_fill;
15     int            event;
16     struct list_head    list;
17 };

再看sysfs_read_file 函数

 1 /**
 2  *    sysfs_read_file - read an attribute. 
 3  *    @file:    file pointer.
 4  *    @buf:    buffer to fill.
 5  *    @count:    number of bytes to read.
 6  *    @ppos:    starting offset in file.
 7  *
 8  *    Userspace wants to read an attribute file. The attribute descriptor
 9  *    is in the file's ->d_fsdata. The target object is in the directory's
10  *    ->d_fsdata.
11  *
12  *    We call fill_read_buffer() to allocate and fill the buffer from the
13  *    object's show() method exactly once (if the read is happening from
14  *    the beginning of the file). That should fill the entire buffer with
15  *    all the data the object has to offer for that attribute.
16  *    We then call flush_read_buffer() to copy the buffer to userspace
17  *    in the increments specified.
18  */
19 
20 static ssize_t
21 sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos)
22 {
23     struct sysfs_buffer * buffer = file->private_data;
24     ssize_t retval = 0;
25 
26     mutex_lock(&buffer->mutex);
27     if (buffer->needs_read_fill || *ppos == 0) {
28         retval = fill_read_buffer(file->f_path.dentry,buffer);
29         if (retval)
30             goto out;
31     }
32     pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n",
33          __func__, count, *ppos, buffer->page);
34     retval = simple_read_from_buffer(buf, count, ppos, buffer->page,
35                      buffer->count);
36 out:
37     mutex_unlock(&buffer->mutex);
38     return retval;
39 }

read处理看着很简单,sysfs_read_file从file指针中取一个私有指针(注:大家可以稍微留一下心,私有数据的概念,在VFS中使用是非常普遍的),转换为一个struct sysfs_buffer类型的指针,以此为参数(buffer),转身就调用fill_read_buffer接口。

 1 /**
 2  *    fill_read_buffer - allocate and fill buffer from object.
 3  *    @dentry:    dentry pointer.
 4  *    @buffer:    data buffer for file.
 5  *
 6  *    Allocate @buffer->page, if it hasn't been already, then call the
 7  *    kobject's show() method to fill the buffer with this attribute's 
 8  *    data. 
 9  *    This is called only once, on the file's first read unless an error
10  *    is returned.
11  */
12 static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer)
13 {
14     struct sysfs_dirent *attr_sd = dentry->d_fsdata;
15     struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
16     const struct sysfs_ops * ops = buffer->ops;
17     int ret = 0;
18     ssize_t count;
19 
20     if (!buffer->page)
21         buffer->page = (char *) get_zeroed_page(GFP_KERNEL);
22     if (!buffer->page)
23         return -ENOMEM;
24 
25     /* need attr_sd for attr and ops, its parent for kobj */
26     if (!sysfs_get_active(attr_sd))
27         return -ENODEV;
28 
29     buffer->event = atomic_read(&attr_sd->s_attr.open->event);
30     count = ops->show(kobj, attr_sd->s_attr.attr, buffer->page);//重要的点,最终call的是ops中show,ops就是struct sysfs_ops,ops哪里来的呢?就是sysfs_open_file时传入的。
31 
32     sysfs_put_active(attr_sd);
33 
34     /*
35      * The code works fine with PAGE_SIZE return but it's likely to
36      * indicate truncated result or overflow in normal use cases.
37      */
38     if (count >= (ssize_t)PAGE_SIZE) {
39         print_symbol("fill_read_buffer: %s returned bad count\n",
40             (unsigned long)ops->show);
41         /* Try to struggle along */
42         count = PAGE_SIZE - 1;
43     }
44     if (count >= 0) {
45         buffer->needs_read_fill = 0;
46         buffer->count = count;
47     } else {
48         ret = count;
49     }
50     return ret;
51 }

而fill_read_buffer接口,直接从buffer指针中取出一个struct sysfs_ops指针,调用该指针的show函数,即完成了文件的read操作。

那么后续呢?当然是由ops->show接口接着处理咯。而具体怎么处理,就是其它模块(例如某个driver)的事了,sysfs不再关心(其实,Linux大多的核心代码,都是只提供架构和机制,具体的实现,也就是苦力,留给那些码农吧!这就是设计的魅力)。

结构体定义位于:include\linux\sysfs.h

1 struct sysfs_ops {
2     ssize_t    (*show)(struct kobject *, struct attribute *,char *);
3     ssize_t    (*store)(struct kobject *,struct attribute *,const char *, size_t);
4     const void *(*namespace)(struct kobject *, const struct attribute *);
5 };

那struct sysfs_ops指针哪来的呢?就是从sysfs_open_file函数中赋值的。接着来看sysfs_file_open函数

 1 struct sysfs_buffer {
 2     size_t            count;
 3     loff_t            pos;
 4     char            * page;
 5     const struct sysfs_ops    * ops;
 6     struct mutex        mutex;
 7     int            needs_read_fill;
 8     int            event;
 9     struct list_head    list;
10 };
 1 static int sysfs_open_file(struct inode *inode, struct file *file)
 2 {
 3     struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
 4     struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
 5     struct sysfs_buffer *buffer;
 6     const struct sysfs_ops *ops;
 7     int error = -EACCES;
 8 
 9     /* need attr_sd for attr and ops, its parent for kobj */
10     if (!sysfs_get_active(attr_sd))
11         return -ENODEV;
12 
13     /* every kobject with an attribute needs a ktype assigned */
14     if (kobj->ktype && kobj->ktype->sysfs_ops)
15         ops = kobj->ktype->sysfs_ops;//拿到sysfs_read_file()所需的ops指针,原来是从attribute所从属kobj中的ktype中获取的
16     else {
17         WARN(1, KERN_ERR "missing sysfs attribute operations for "
18                "kobject: %s\n", kobject_name(kobj));
19         goto err_out;//如果从属的kobject(就是attribute文件所在的目录)没有ktype,或者没有ktype->sysfs_ops指针,是不允许它注册任何attribute的
20     }
21 
22     /* File needs write support.
23      * The inode's perms must say it's ok, 
24      * and we must have a store method.
25      */
26     if (file->f_mode & FMODE_WRITE) {
27         if (!(inode->i_mode & S_IWUGO) || !ops->store)
28             goto err_out;
29     }
30 
31     /* File needs read support.
32      * The inode's perms must say it's ok, and we there
33      * must be a show method for it.
34      */
35     if (file->f_mode & FMODE_READ) {
36         if (!(inode->i_mode & S_IRUGO) || !ops->show)
37             goto err_out;
38     }
39 
40     /* No error? Great, allocate a buffer for the file, and store it
41      * it in file->private_data for easy access.
42      */
43     error = -ENOMEM;
44     buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL);
45     if (!buffer)
46         goto err_out;
47 
48     mutex_init(&buffer->mutex);
49     buffer->needs_read_fill = 1;
50     buffer->ops = ops;//再把ops赋给buffer
51     file->private_data = buffer;//最后把buffer赋值给file的私有数据,sysfs_read_file()就是从file的私有化数据中拿到的ops。
52 
53     /* make sure we have open dirent struct */
54     error = sysfs_get_open_dirent(attr_sd, buffer);
55     if (error)
56         goto err_free;
57 
58     /* open succeeded, put active references */
59     sysfs_put_active(attr_sd);
60     return 0;
61 
62  err_free:
63     kfree(buffer);
64  err_out:
65     sysfs_put_active(attr_sd);
66     return error;
67 }

我们注意一下14行的注释以及其后代码逻辑,如果从属的kobject(就是attribute文件所在的目录)没有ktype,或者没有ktype->sysfs_ops指针,是不允许它注册任何attribute的!

经过确认后,sysfs_open_file从ktype中取出struct sysfs_ops指针,并在随后的代码逻辑中,分配一个struct sysfs_buffer类型的指针(buffer),并把struct sysfs_ops指针保存在其中,随后(注意哦),把buffer指针交给file的private_data,随后read/write等接口便可以取出使用。驱动的open函数也会用到类似机制。嗯!惯用伎俩!

位于:include\linux\sysfs.h

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

attribute文件的write过程和read类似,这里就不再多说。另外,上面只分析了普通attribute的逻辑,而二进制类型的呢?也类似,去看看fs/sysfs/bin.c吧,这里也不说了。

讲到这里,应该已经结束了,事实却不是如此。上面read/write的数据流,只到kobject(也就是目录)级别哦,而真正需要操作的是attribute(文件)啊!这中间一定还有一层转换!确实,不过又交给其它模块了。 下面我们通过一个例子,来说明如何转换的。

3. sysfs在设备模型中的应用总结

3.1 class

首先,在class.c中,定义了Class所需的ktype以及sysfs_ops类型的变量,让我们通过设备模型class.c中有关sysfs的实现,来总结一下sysfs的应用方式。如下:

 1 static const struct sysfs_ops class_sysfs_ops = {
 2     .show    = class_attr_show,        
 3     .store    = class_attr_store,
 4 };
 5  
 6 /*  */
 7 static struct kobj_type class_ktype = {
 8     .sysfs_ops    = &class_sysfs_ops,
 9     .release    = class_release,
10     .child_ns_type    = class_child_ns_type,
11 };

所有class_type的Kobject下面的attribute文件的读写操作,都会交给class_attr_show和class_attr_store两个接口处理。以class_attr_show为例:

 1 #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
 2  
 3  
 4 static ssize_t class_attr_show(struct kobject *kobj, struct attribute *attr,
 5                    char *buf)
 6 {
 7     struct class_attribute *class_attr = to_class_attr(attr);
 8     struct class_private *cp = to_class(kobj);
 9     ssize_t ret = -EIO;
10  
11     if (class_attr->show)
12         ret = class_attr->show(cp->class, class_attr, buf);
13     return ret;
14 }

该接口使用container_of从struct attribute类型的指针中取得一个class模块的自定义指针:struct class_attribute,该指针中包含了class模块自身的show和store接口。下面是struct class_attribute的声明:

1 struct class_attribute {
2     struct attribute attr;
3     ssize_t (*show)(struct class *class, struct class_attribute *attr,
4             char *buf);
5     ssize_t (*store)(struct class *class, struct class_attribute *attr,
6             const char *buf, size_t count);
7 }

因此,所有需要使用attribute的模块,都不会直接定义struct attribute变量,而是通过一个自定义的数据结构,该数据结构的一个成员是struct attribute类型的变量,并提供show和store回调函数。然后在该模块ktype所对应的struct sysfs_ops变量中,实现该本模块整体的show和store函数,并在被调用时,转接到自定义数据结构(struct class_attribute)中的show和store函数中。这样,每个atrribute文件,实际上对应到一个自定义数据结构变量中了。

再看一下attribute,它本身并不具备任何的接口,所以它作为底层和kobject一样,都要绑定在具体的实例(device或driver)中使用。

1 struct attribute {
2     const char        *name;
3     struct module        *owner;
4     mode_t            mode;
5 #ifdef CONFIG_DEBUG_LOCK_ALLOC
6     struct lock_class_key    *key;
7     struct lock_class_key    skey;
8 #endif
9 };

struct class中包含device_attribute 和class_attribute        

 1 struct class {
 2     const char        *name;
 3     struct module        *owner;
 4 
 5     struct class_attribute        *class_attrs;
 6     struct device_attribute        *dev_attrs;
 7     struct bin_attribute        *dev_bin_attrs;
 8     struct kobject            *dev_kobj;        
 9   ...
10 }

 

3.2 devices

include\linux\device.h中,自定义一个定义属性结构体,其中包含struct attribute类型。

1 /* interface for exporting device attributes */
2 struct device_attribute {
3     struct attribute    attr;
4     ssize_t (*show)(struct device *dev, struct device_attribute *attr,
5             char *buf);
6     ssize_t (*store)(struct device *dev, struct device_attribute *attr,
7              const char *buf, size_t count);
8 };

drivers\base\core.c 中给自定义的device属性结构体赋值。

#define DEVICE_ATTR(_name, _mode, _show, _store) \
    struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
static struct device_attribute devt_attr =
    __ATTR(dev, S_IRUGO, show_dev, NULL);

在device_add()中创建

1 int device_add(struct device *dev)
2 {
3   ...
4     if (MAJOR(dev->devt)) {
5         error = device_create_file(dev, &devt_attr);
6         if (error)
7             goto ueventattrError;
8   ...
9 

3.3device_driver

同上,自定义属性结构体driver_attribute ,中包含属性结构体。

1 /* sysfs interface for exporting driver attributes */
2 
3 struct driver_attribute {
4     struct attribute attr;
5     ssize_t (*show)(struct device_driver *driver, char *buf);
6     ssize_t (*store)(struct device_driver *driver, const char *buf,
7              size_t count);
8 };

用下面宏给driver属性结构体赋值

#define DRIVER_ATTR(_name, _mode, _show, _store)    \
struct driver_attribute driver_attr_##_name =        \
    __ATTR(_name, _mode, _show, _store)

driver属性结构体作为driver_create_file()的参数在drivers中使用。

 1 int driver_create_file(struct device_driver *drv,
 2                const struct driver_attribute *attr)
 3 {
 4     int error;
 5     if (drv)
 6         error = sysfs_create_file(&drv->p->kobj, &attr->attr);
 7     else
 8         error = -EINVAL;
 9     return error;
10 }

3.4 bus

1 struct bus_attribute {
2     struct attribute    attr;
3     ssize_t (*show)(struct bus_type *bus, char *buf);
4     ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
5 };

 

bus结构体bus_type中还分别包含device和driver的属性结构体 

1 struct bus_type {
2     const char        *name;
3     const char        *dev_name;
4     struct device        *dev_root;
5     struct bus_attribute    *bus_attrs;
6     struct device_attribute    *dev_attrs;
7     struct driver_attribute    *drv_attrs;
8         ...
9 }

bus属性结构体作为bus_create_file()的参数在bus中使用。

 1 int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
 2 {
 3     int error;
 4     if (bus_get(bus)) {
 5         error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
 6         bus_put(bus);
 7     } else
 8         error = -EINVAL;
 9     return error;
10 }

 3.5 kobject

同上也是自定义了一个属性结构体,include\linux\kobject.h

1 struct kobj_attribute {
2     struct attribute attr;
3     ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
4             char *buf);
5     ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
6              const char *buf, size_t count);
7 };
struct attribute作为kobj_attr_show()和kobj_attr_store()的参数,然后这两个函数里面实际调用的是kobj_attribute的show、store函数。
 1 /* default kobject attribute operations */
 2 static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
 3                   char *buf)
 4 {
 5     struct kobj_attribute *kattr;
 6     ssize_t ret = -EIO;
 7 
 8     kattr = container_of(attr, struct kobj_attribute, attr);//根据kobj_attribute中成员attribute拿到kobj_attribute
 9     if (kattr->show)
10         ret = kattr->show(kobj, kattr, buf);//调用kobj_attribute中的show函数
11     return ret;
12 }
13 
14 static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,
15                    const char *buf, size_t count)
16 {
17     struct kobj_attribute *kattr;
18     ssize_t ret = -EIO;
19 
20     kattr = container_of(attr, struct kobj_attribute, attr);
21     if (kattr->store)
22         ret = kattr->store(kobj, kattr, buf, count);
23     return ret;
24 }
25 
26 const struct sysfs_ops kobj_sysfs_ops = {
27     .show    = kobj_attr_show,
28     .store    = kobj_attr_store,
29 };

 

kobj_sysfs_ops有作为结构体kobject的成员,这样kobj_attribute和kobject联系了起来。

感谢博友分享

博友链接:https://blog.csdn.net/qq_16777851/java/article/details/81396047

推荐阅读