linux設備驅動(9)attribute詳解


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM