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