# ssd1306_linux_i2c **Repository Path**: kris2wu/ssd1306_linux_i2c ## Basic Information - **Project Name**: ssd1306_linux_i2c - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-08-11 - **Last Updated**: 2021-08-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 每个i2c总线目录下面会提供两个文件节点: new_device, delete_device 内核必须要知道在设备挂在哪个总线和设备地址才能实例化该设备。 具体的做法如下: 增加“oled”设备,地址为0x3C: echo oled 0x3C > /sys/bus/i2c/devices/i2c-0/new_device 删除 “oled”设备: echo 0x3C > /sys/bus/i2c/devices/i2c-0/delete_device 一般: echo oled 0x3C > /sys/bus/i2c/devices/i2c-0/new_device //生成i2c_client insmod oled-ssd1306.ko echo helloworld > /sys/bus/i2c/devices/i2c-0/0-003c/oled_attr //在sys下的对某个文件进行cat和echo是调用在用sysfs_create_file创建文件的时候,//填的第二参数;里面有实现show和store的函数 在sys下的对某个文件进行cat和echo是调用在用sysfs_create_file创建文件的时候,填的第二参数;里面有实现show和store的函数 device_create | + -- kzalloc struct device | +---device_register | +-----device_initialize | +-----device_add --------------------- device_register()函数会在/sys/devices下创建一个对应的目录; bus_create_file()是在sysfs下创建一个文件夹 create_dir(kobj),这个函数作用是在sysfs下创建一个文件夹 device_create函数来在/dev目录下创建相应的设备节点 加载模块的时候,用户空间中的udev会自动响应 device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点 uevent根据dev的属性文件在/dev下创建文件 而uevent把事件上报给用户空间有两种途径:   1.通过kmod模块,直接调用用户空间的可执行程序或脚本。   2.通过netlink通信机制,将事件从内核空间传递到用户空间。 kobject_add() -> create_dir() -> sysfs_create_dir() 每当我们新增一个kobject结构的时候,同时会在/sys下创建一个目录。 sysfs_create_file(struct kobject * kobj, const struct attribute * attr) 传给它的参数是kobj和attr,其中,kobject对应的是文件夹,attribute对应的是该文件夹下的文件。 1、目录项(/sys/kernel/helloworld):通过函数kobject_create_and_add("helloworld", kernel_kobj)可以在/sys/kernel下建立一个helloworld目录项。 2、属性文件(hello_value):通过函数sysfs_create_file(helloworld_kobj, &hello_value_attribute)建立。这个也同时建立了文件与操作之间的联系和对应。 3、操作(hello_show、hello_store):在sys系统中对文件的操作有2个函数,一个是show,一个是store,这两个函数和普通文件的read和write函数有点类似,是他们的精简版。对于sprintf和sscanf是对copy_from_user和copy_to_user函数的封装。 sysfs是用于表现设备驱动模型的文件系统,它基于ramfs。要学习linux的设备驱动模型,就要先做好底层工作,总结sysfs提供给外界的API就是其中之一。sysfs文件系统中提供了四类文件的创建与管理,分别是 目录、普通文件、软链接文件、二进制文件。 1. 目录层次往往代表着设备驱动模型的结构, 2. 软链接文件则代表着不同部分间的关系。比如某个设备的目录只出现在/sys/devices下,其它地方涉及到它时只好用软链接文件链接过去,保持了设备唯一的实例。 3. 而普通文件和二进制文件往往代表了设备的属性,读写这些文件需要调用相应的属性读写。 sysfs是表现设备驱动模型的文件系统,它的目录层次实际反映的是对象的层次。为了配合这种目录,linux专门提供了两个结构作为sysfs的骨架,它们就是struct kobject和struct kset。我们知道,sysfs是完全虚拟的,它的每个目录其实都对应着一个kobject,要想知道这个目录下有哪些子目录,就要用到kset。从面向对象的角度来讲,kset继承了kobject的功能,既可以表示sysfs中的一个目录,还可以包含下层目录。对于kobject和kset,会在其它文章中专门分析到,这里简单描述只是为了更好地介绍sysfs提供的API。 ``` sysfs模块提供给外界的接口完全展现在include/linux/sysfs.h中。 struct attribute { const char *name; struct module *owner; mode_t mode; }; struct attribute_group { const char *name; mode_t (*is_visible)(struct kobject *, struct attribute *, int); struct attribute **attrs; }; 之前说过普通文件是kobject目录的属性展现。struct attribute就是属性的通用结构,其它部分在使用时还可以把struct attribute内嵌到更大的属性结构中。struct attribute_group是提供一组属性的集合,这样集中的管理更为方便。 #define __ATTR(_name,_mode,_show,_store) { \ .attr = {.name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ } #define __ATTR_RO(_name) { \ .attr = { .name = __stringify(_name), .mode = 0444 }, \ .show = _name##_show, \ } #define __ATTR_NULL { .attr = { .name = NULL } } #define attr_name(_attr) (_attr).attr.name 以上的宏是为了静态初始化属性时更为方便,我们简单将其忽略。 struct bin_attribute { struct attribute attr; size_t size; void *private; ssize_t (*read)(struct kobject *, struct bin_attribute *, char *, loff_t, size_t); ssize_t (*write)(struct kobject *, struct bin_attribute *, char *, loff_t, size_t); int (*mmap)(struct kobject *, struct bin_attribute *attr, struct vm_area_struct *vma); }; struct attribute是通用的属性结构,而struct bin_attribute就是为二进制属性专门设计的,它在sysfs中表现为二进制文件,大多数是设备配置参数的映射。 struct bin_attribute恰恰就是把struct attribute内嵌到更大结构的样例。 struct sysfs_ops { ssize_t (*show)(struct kobject *, struct attribute *,char *); ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t); }; struct sysfs_ops中包含show和store两个函数指针,它们分别在sysfs文件读和文件写时调用。 int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), void *data, struct module *owner); sysfs_schedule_callback()会创建一个工作队列,稍后调用func(data)。本来sysfs中的属性读写函数是无法删除属性文件或者kobject目录的,因为调用函数时是加锁的,要删除也需要加锁。但这里可以通过工作队列回调的方式实现。 int __must_check sysfs_create_dir(struct kobject *kobj); void sysfs_remove_dir(struct kobject *kobj); int __must_check sysfs_rename_dir(struct kobject *kobj, const char *new_name); int __must_check sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj); sysfs_create_file()在kobj对应的目录下创建attr对应的属性文件。 sysfs_chmod_file()修改attr对应的属性文件的读写权限。 sysfs_remove_file()在kobj对应的目录下删除attr对应的属性文件。 int __must_check sysfs_create_bin_file(struct kobject *kobj, struct bin_attribute *attr); void sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr); sysfs_create_bin_file()在kobj目录下创建attr对应的二进制属性文件。 sysfs_remove_bin_file()在kobj目录下删除attr对应的二进制属性文件。 int __must_check sysfs_create_link(struct kobject *kobj, struct kobject *target, const char *name); int __must_check sysfs_create_link_nowarn(struct kobject *kobj, struct kobject *target, const char *name); void sysfs_remove_link(struct kobject *kobj, const char *name); sysfs_create_link()在kobj目录下创建指向target目录的软链接,name为软链接文件名称。 sysfs_create_link_nowarn()与sysfs_create_link()功能相同,只是在软链接文件已存在时不会出现警告。 sysfs_remove_link()删除kobj目录下名为name的软链接文件。 int __must_check sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp); int sysfs_update_group(struct kobject *kobj, const struct attribute_group *grp); void sysfs_remove_group(struct kobject *kobj, const struct attribute_group *grp); int sysfs_add_file_to_group(struct kobject *kobj, const struct attribute *attr, const char *group); void sysfs_remove_file_from_group(struct kobject *kobj, const struct attribute *attr, const char *group); sysfs_create_group()在kobj目录下创建一个属性集合,并显示集合中的属性文件。如果文件已存在,会报错。 sysfs_update_group()在kobj目录下创建一个属性集合,并显示集合中的属性文件。文件已存在也不会报错。sysfs_update_group()也用于group改动影响到文件显示时调用。 sysfs_remove_group()在kobj目录下删除一个属性集合,并删除集合中的属性文件。 sysfs_add_file_to_group()将一个属性attr加入kobj目录下已存在的的属性集合group。 sysfs_remove_file_from_group()将属性attr从kobj目录下的属性集合group中删除。 void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr); void sysfs_notify_dirent(struct sysfs_dirent *sd); sysfs_notify()和sysfs_notify_dirent()都是用来唤醒在属性文件上调用select()或poll()而阻塞的用户进程。 struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, const unsigned char *name); struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd); void sysfs_put(struct sysfs_dirent *sd); sysfs_get()增加目录或文件的引用计数。 sysfs_put()减少目录或文件的引用计数,并在降为零时删除相应的文件或目录,这种删除又会减少上层目录的引用计数。 sysfs_get_dirent()是增加目录parent_sd中名为name的目录或文件的引用计数。 虽然同样是引用计数,同样在降为零时有删除动作,但却并非使用kref。这种操作更多地继承了文件系统管理时的传统。 void sysfs_printk_last_file(void); int __must_check sysfs_init(void); sysfs_printk_last_file()是在sysfs崩溃时打印最后一个访问到的文件路径。 sysfs_init()是在sysfs模块初始化时调用的。 这两个函数都与我们没有什么关系。 sysfs是一个基于RAM的文件系统,它和Kobject一起,可以将Kernel的数据结构导出到用户空间,以文件目录结构的形式,提供对这些数据结构(以及数据结构的属性)的访问支持。 sysfs具备文件系统的所有属性,而本文主要侧重其设备模型的特性,因此不会涉及过多的文件系统实现细节,而只介绍sysfs在Linux设备模型中的作用和使用方法。具体包括: sysfs和Kobject的关系 attribute的概念 sysfs的文件系统操作接口 2. sysfs和Kobject的关系 在"Linux设备模型_Kobject”文章中,有提到过,每一个Kobject,都会对应sysfs中的一个目录。因此在将Kobject添加到Kernel时,create_dir接口会调用sysfs文件系统的创建目录接口,创建和Kobject对应的目录,相关的代码如下: 1: /* lib/kobject.c, line 47 */ 2: static int create_dir(struct kobject *kobj) 3: { 4: int error = 0; 5: error = sysfs_create_dir(kobj); 6: if (!error) { 7: error = populate_dir(kobj); 8: if (error) 9: sysfs_remove_dir(kobj); 10: } 11: return error; 12: } 13: 14: /* fs/sysfs/dir.c, line 736 */ 15: ** 16: * sysfs_create_dir - create a directory for an object. 17: * @kobj: object we're creating directory for. 18: */ 19: int sysfs_create_dir(struct kobject * kobj) 20: { 21: enum kobj_ns_type type; 22: struct sysfs_dirent *parent_sd, *sd; 23: const void *ns = NULL; 24: int error = 0; 25: ... 26: } 3. attribute 3.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,如下: 1: /* include/linux/sysfs.h, line 26 */ 2: struct attribute { 3: const char *name; 4: umode_t mode; 5: #ifdef CONFIG_DEBUG_LOCK_ALLOC 6: bool ignore_lockdep:1; 7: struct lock_class_key *key; 8: struct lock_class_key skey; 9: #endif 10: }; 11: 12: /* include/linux/sysfs.h, line 100 */ 13: struct bin_attribute { 14: struct attribute attr; 15: size_t size; 16: void *private; 17: ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *, 18: char *, loff_t, size_t); 19: ssize_t (*write)(struct file *,struct kobject *, struct bin_attribute *, 20: char *, loff_t, size_t); 21: int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr, 22: struct vm_area_struct *vma); 23: }; struct attribute为普通的attribute,使用该attribute生成的sysfs文件,只能用字符串的形式读写(后面会说为什么)。而struct bin_attribute在struct attribute的基础上,增加了read、write等函数,因此它所生成的sysfs文件可以用任何方式读写。 说完基本概念,我们要问两个问题: Kernel怎么把attribute变成sysfs中的文件呢? 用户空间对sysfs的文件进行的读写操作,怎么传递给Kernel呢? 下面来看看这个过程。 3.2 attibute文件的创建 在linux内核中,attibute文件的创建是由fs/sysfs/file.c中sysfs_create_file接口完成的,该接口的实现没有什么特殊之处,大多是文件系统相关的操作,和设备模型没有太多的关系,这里先略过不提。 3.3 attibute文件的read和write 看到3.1章节struct attribute的原型时,也许我们会犯嘀咕,该结构很简单啊,name表示文件名称,mode表示文件模式,其它的字段都是内核用于debug Kernel Lock的,那文件操作的接口在哪里呢? 不着急,我们去fs/sysfs目录下看看sysfs相关的代码逻辑。 所有的文件系统,都会定义一个struct file_operations变量,用于描述本文件系统的操作接口,sysfs也不例外: 1: /* fs/sysfs/file.c, line 472 */ 2: const struct file_operations sysfs_file_operations = { 3: .read = sysfs_read_file, 4: .write = sysfs_write_file, 5: .llseek = generic_file_llseek, 6: .open = sysfs_open_file, 7: .release = sysfs_release, 8: .poll = sysfs_poll, 9: }; attribute文件的read操作,会由VFS转到sysfs_file_operations的read(也就是sysfs_read_file)接口上,让我们大概看一下该接口的处理逻辑。 1: /* fs/sysfs/file.c, line 127 */ 2: static ssize_t 3: sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) 4: { 5: struct sysfs_buffer * buffer = file->private_data; 6: ssize_t retval = 0; 7: 8: mutex_lock(&buffer->mutex); 9: if (buffer->needs_read_fill || *ppos == 0) { 10: retval = fill_read_buffer(file->f_path.dentry,buffer); 11: if (retval) 12: goto out; 13: } 14: ... 15: } 16: /* fs/sysfs/file.c, line 67 */ 17: static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) 18: { 19: struct sysfs_dirent *attr_sd = dentry->d_fsdata; 20: struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; 21: const struct sysfs_ops * ops = buffer->ops; 22: ... 23: count = ops->show(kobj, attr_sd->s_attr.attr, buffer->page); 24: ... 25: } read处理看着很简单,sysfs_read_file从file指针中取一个私有指针(注:大家可以稍微留一下心,私有数据的概念,在VFS中使用是非常普遍的),转换为一个struct sysfs_buffer类型的指针,以此为参数(buffer),转身就调用fill_read_buffer接口。 而fill_read_buffer接口,直接从buffer指针中取出一个struct sysfs_ops指针,调用该指针的show函数,即完成了文件的read操作。 那么后续呢?当然是由ops->show接口接着处理咯。而具体怎么处理,就是其它模块(例如某个driver)的事了,sysfs不再关心(其实,Linux大多的核心代码,都是只提供架构和机制,具体的实现,也就是苦力,留给那些码农吧!这就是设计的魅力)。 不过还没完,这个struct sysfs_ops指针哪来的?好吧,我们再看看open(sysfs_open_file)接口吧。 1: /* fs/sysfs/file.c, line 326 */ 2: static int sysfs_open_file(struct inode *inode, struct file *file) 3: { 4: struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 5: struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; 6: struct sysfs_buffer *buffer; 7: const struct sysfs_ops *ops; 8: int error = -EACCES; 9: 10: /* need attr_sd for attr and ops, its parent for kobj */ 11: if (!sysfs_get_active(attr_sd)) 12: return -ENODEV; 13: 14: /* every kobject with an attribute needs a ktype assigned */ 15: if (kobj->ktype && kobj->ktype->sysfs_ops) 16: ops = kobj->ktype->sysfs_ops; 17: else { 18: WARN(1, KERN_ERR "missing sysfs attribute operations for " 19: "kobject: %s\n", kobject_name(kobj)); 20: goto err_out; 21: } 22: 23: ... 24: 25: buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL); 26: if (!buffer) 27: goto err_out; 28: 29: mutex_init(&buffer->mutex); 30: buffer->needs_read_fill = 1; 31: buffer->ops = ops; 32: file->private_data = buffer; 33: ... 34: } 哦,原来和ktype有关系。这个指针是从该attribute所从属的kobject中拿的。再去看一下"Linux设备模型_Kobject”中ktype的定义,还真有一个struct sysfs_ops的指针。 我们注意一下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等接口便可以取出使用。嗯!惯用伎俩! 顺便看一下struct sysfs_ops吧,我想你已经能够猜到了。 1: /* include/linux/sysfs.h, line 124 */ 2: struct sysfs_ops { 3: ssize_t (*show)(struct kobject *, struct attribute *,char *); 4: ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t); 5: const void *(*namespace)(struct kobject *, const struct attribute *); 6: }; attribute文件的write过程和read类似,这里就不再多说。另外,上面只分析了普通attribute的逻辑,而二进制类型的呢?也类似,去看看fs/sysfs/bin.c吧,这里也不说了。 讲到这里,应该已经结束了,事实却不是如此。上面read/write的数据流,只到kobject(也就是目录)级别哦,而真正需要操作的是attribute(文件)啊!这中间一定还有一层转换!确实,不过又交给其它模块了。 下面我们通过一个例子,来说明如何转换的。 4. sysfs在设备模型中的应用总结 让我们通过设备模型class.c中有关sysfs的实现,来总结一下sysfs的应用方式。 首先,在class.c中,定义了Class所需的ktype以及sysfs_ops类型的变量,如下: 1: /* drivers/base/class.c, line 86 */ 2: static const struct sysfs_ops class_sysfs_ops = { 3: .show = class_attr_show, 4: .store = class_attr_store, 5: .namespace = class_attr_namespace, 6: }; 7: 8: static struct kobj_type class_ktype = { 9: .sysfs_ops = &class_sysfs_ops, 10: .release = class_release, 11: .child_ns_type = class_child_ns_type, 12: }; 由前面章节的描述可知,所有class_type的Kobject下面的attribute文件的读写操作,都会交给class_attr_show和class_attr_store两个接口处理。以class_attr_show为例: 1: /* drivers/base/class.c, line 24 */ 2: #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr) 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 subsys_private *cp = to_subsys_private(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: /* include/linux/device.h, line 399 */ 2: struct class_attribute { 3: struct attribute attr; 4: ssize_t (*show)(struct class *class, struct class_attribute *attr, 5: char *buf); 6: ssize_t (*store)(struct class *class, struct class_attribute *attr, 7: const char *buf, size_t count); 8: const void *(*namespace)(struct class *class, 9: const struct class_attribute *attr); 10: }; 因此,所有需要使用attribute的模块,都不会直接定义struct attribute变量,而是通过一个自定义的数据结构,该数据结构的一个成员是struct attribute类型的变量,并提供show和store回调函数。然后在该模块ktype所对应的struct sysfs_ops变量中,实现该本模块整体的show和store函数,并在被调用时,转接到自定义数据结构(struct class_attribute)中的show和store函数中。这样,每个atrribute文件,实际上对应到一个自定义数据结构变量中了。 ysfs是一个基于RAM的文件系统,它和kobject一块儿,能够将Kernel的数据结构导出到用户空间,以文件目录结构的形式,提供对这些数据结构(以及数据结构的属性)的访问支持。node sysfs目录linux 所包含内容编程 /sys/devices缓存 这是内核对系统中全部设备的分层次表达模型,也是 /sys 文件系统管理设备的最重要的目录结构数据结构 /sys/dev函数 这个目录下维护一个按字符设备和块设备的主次号码(major:minor)连接到真实的设备(/sys/devices下)的符号连接文件atom /sys/busspa 这是内核设备按总线类型分层放置的目录结构, devices 中的全部设备都是链接于某种总线之下,在这里的每一种具体总线之下能够找到每个具体设备的符号连接,它也是构成Linux统一设备模型的一部分;设计 /sys/class3d 这是按照设备功能分类的设备模型,如系统全部输入设备都会出如今 /sys/class/input之下,而不论它们是以何种总线链接到系统。它也是构成Linux统一设备模型的一部分; /sys/block(stale) 这里是系统中当前全部的块设备所在,按照功能来讲放置在 /sys/class 之下会更合适,但只是因为历史遗留因素而一直存在于/sys/block,但从2.6.22开始就已标记为过期,只有在打开了CONFIG_SYSFS_DEPRECATED配置下编译才会有这个目录的存在,而且在2.6.26内核中已正式移到/sys/class/block,旧的接口/sys/block为了向后兼容保留存在,但其中的内容已经变为指向它们在/sys/devices/中真实设备的符号连接文件; /sys/firmware 这里是系统加载固件机制的对用户空间的接口,关于固件有专用于固件加载的一套API /sys/fs 这里按照设计是用于描述系统中全部文件系统,包括文件系统自己和按文件系统分类存放的已挂载点; /sys/kernel 这里是内核全部可调整参数的位置; /sys/module 这里有系统中全部模块的信息,不论这些模块是之内联(inlined)方式编译到内核映像文件(vmlinuz)中仍是编译为外部模块(ko文件),均可能会出如今/sys/module中: 编译为外部模块(ko文件)在加载后会出现对应的/sys/module//,而且在这个目录下会出现一些属性文件和属性目录来表示此外部模块的一些信息,如版本号、加载状态、所提供的驱动程序等; 编译为内联方式的模块则只在当它有非0属性的模块参数时会出现对应的/sys/module/,这些模块的可用参数会出如今/sys/modules//parameters/中, 如 /sys/module/printk/parameters/time 这个可读写参数控制着内联模块printk在打印内核消息时是否加上时间前缀; 全部内联模块的参数也能够由 ".="的形式写在内核启动参数上,如启动内核时加上参数"printk.time=1"与 向"/sys/module/printk/parameters/time"写入1的效果相同; 没有非0属性参数的内联模块不会出现于此。 /sys/power 这里是系统中电源选项,这个目录下有几个属性文件能够用于控制整个机器的电源状态,如能够向其中写入控制命令让机器关机、重启等。 前面已经讲过sysfs和kobject等的关系了,这里再也不赘述,下面主要说一下属性和sysfs与普通文件系统的关系。 前面已经看到的跟属性相关的关键数据结构有: 1 attribute 2 bin_attribute 3 attribute_group 1.普通属性 name,属性的名字,对应sysfs中文件的名字。 mode,应用于属性(文件)的保护位,与文件的权限相同。 2.二进制属性 attr,包含的普通属性。 size,二进制属性的最大长度,若是没有最大值能够设为0。 private,私有数据,与linux中大部分私有数据的用途同样,用于传递私有数据结构。 read()、write()、mmap(),操做二进制属性的函数。 使用该attribute生成的sysfs文件,只能用字符串的形式读写。而struct bin_attribute在struct attribute的基础上,增长了read、write等函数,所以它所生成的sysfs文件能够用任何方式读写。固件通常使用bin_attribute属性。 3.属性组 name,属性组的名字,不为空的话对应一个sysfs的文件夹。 attrs,属性组里的普通属性列表。 bin_attrs,属性组里的二进制属性列表。 is_visible(),返回组中属性的读写权限。 sysfs_ops 属性经过sysfs_ops进行读写。 show,显示属性值。 store,保存属性值。 sysfs与普通文件系统的关系 sysfs中的目录和文件与kobject和attribute对应,而普通文件系统是file和inode。sysfs在上层也是经过普通的read(),write()等系统调用进行操做的,那么须要将file_operations转换成最终的show()/store()。怎么转?由于属性都是属于kobject的,因此最后的读写确定与kobject有关,先从kobject找找线索,看前面kobject有个属性——sd,kernfs_node数据结构,是kobject在sysfs的表示。咱们从分析这个核心数据结构入手。 kernfs_node count、active,相关的计数,原子的。 parent,本节点的父节点,这个比较重要,属性是文件,父节点才是kobject。 name,节点名字。 rb,红黑树节点。 ns、has,命名空间相关。 dir、symlink、attr,节点的类型,表示该节点是目录、符号连接仍是属性。三个属性定义在联合体中,咱们在这关注是的attr。 priv,私有数据,看到私有数据应该推测,有用的东西就在这里传递,事实也是如此,在本节描述的访问属性的场景下,kobject就做为私有数据随kernfs_node传递。 flags、mode,与文件的相关属性含义一致。 ino,推测是子设备号。 iattr,节点自己的属性。 那么file如何转换成kernfs_node的呢,类比一下,普通文件有file和inode,这里kernfs_node应该是至关于inode,那还应该有表明文件的结构与之对应,没错,是有这个结构。这个结构是什么,不妨先在kernfs_node中找线索。既然咱们的研究对象是属性,那就看看联合里的attr对应的数据结构。 kernfs_elem_attr ops,对于kernfs的操做函数。 open,打开的kernfs_node。 size,没有查到用途,不影响分析。 notify_next,通知kernfs文件,具体功能不详,不影响分析。 只关注ops,目前linux中基本全部的操做都独立抽象出了数据结构,这个数据结构应该是操做kernfs的函数,可能与咱们找的答案有关。 kernfs_ops atomic_write_len,写是以kernel page为边界的,若是没有设置atomic_write_len,超过PAGE_SIZE的写操做会被分红几个操做进行。若是设置了atomic_write_len,那么超过这个设置值的写操做,atomic_write_len之内的部分是原子写,超过的部分直接返回-E2BIG。 prealloc,设置了用mmap,不设置用read/write。由于read/wirte有本身的buf,与prealloc的buf不兼容。 seq_show、seq_start、seq_next、seq_stop,顺序文件操做。下面会详细分析。这里须要关注他们的参数。 read、write、mmap,读,写,内存映射函数,这里关注他们的参数。 看一下seq_xx和read的原型,参数分别为seq_file和kernfs_open_file。从名字上看kernfs_open_file和kernfs_node的关系,很像file和inode的关系。不妨先看看kernfs_open_file。 kernfs_open_file kn,所属的目录节点。 file,表明打开文件。 priv,私有数据结构。 mutex,互斥体。 list,确实没有查到在哪里调用,暂时推测不出用途,不影响分析。 pralloc_buf,mmap使用。 atomic_write_len,同kernfs_elem_attr。 mmaped,是否已经进行了ioremap。 vm_ops,虚拟内存管理操做。 从核心类的成员可能得出这样的推测,kernfs_open_file.file存储普通文件的file指针,在进行读写是用container_of()从file里得到kernfs_open_file结构体。之前的内核多是这么作的,可是如今不是这样的。这里要提到上面数的另外一个核心数据结构seq_file。 seq_file seq_file是为proc文件系统设计的,来历是这样的,因为procfs的默认操做函数只使用一页的缓存,在处理较大的proc文件时就有点麻烦,而且在输出一系列结构体中的数据时也比较不灵活,须要本身在read_proc函数中实现迭代,容易出现Bug。因此内核黑客们对一些/proc代码作了研究,抽象出共性,最终造成了seq_file(Sequence file:序列文件)接口。 这个接口提供了一套简单的函数来解决以上proc接口编程时存在的问题,使得编程更加容易,下降了Bug出现的机会。 不仅是proc,在须要建立一个由一系列数据顺序组合而成的虚拟文件或一个较大的虚拟文件时,推荐使用seq_file接口。这正是符合bin_attribute的场景。看一下seq_file的结构 这个结构的成员与file差很少,基本不用关心,由于他们都是使用op成员定义的方法操做的,必定意义上能够看做私有成员,图中没有这么画。 须要注意的是结构里有个private,这么多回了应该有这个意识了,看见private就有可能跟咱们结构挂上关系。事实也是如此。kernfs_open_file的指针是放到seq_file的private中的。 从file到attribute 至此只剩seq_file如何与普通的file联系在一块儿了,那就简单了,跟大部分驱动程序是同样的,seq_file包含在file的private_data中。整合上述关系,能够在下面类图中表示。 file | V seq_file | V kernfs_open_file . .kobject | . . . . /\ V . . | kernfs_node attribute 从file找到sysfs的属性文件,经历了很长的链条,这个其实是多态,由于c语言的特性,linux驱动程序用了很费劲的方式实现了多态。 上边这个可能还是抽象一点,把ops族对象加入跟为直观,忽略一些中间环节。 file_operations | v file kernfs_ops | | | V V | sysfs_ops seq_file | | | | | | | V v V | kernfs_open_file . .kobject | | . . . . | | V . . | | kernfs_node attribute 从操作上,从VFS的fs到sysfs经历了file_operations域,kernfs_ops域和最终的sys_ops域的转换。 ```