sysfs文件系统
sysfs,全称为System Filesystem,是一个由Linux内核实现的虚拟文件系统。它扮演着一个桥梁的角色,将内核中的设备和驱动程序信息以文件的形式呈现给用户空间。与/proc文件系统相似,sysfs专注于展示设备和驱动程序的细节,而/proc则主要反映进程信息。
在sysfs中,信息被组织成层次化的文件系统结构。每个设备或内核对象在文件系统中都有其对应的表示,通常是以文件或目录的形式存在。这些文件不仅存储了设备的属性和状态信息,而且很多文件还允许用户通过读写操作来对设备进行配置或控制。
sysfs默认挂载在文件系统的/sys路径下。通过这个虚拟文件系统,用户空间的应用程序能够以一种直观和动态的方式访问和管理系统设备,无需进行复杂的内核空间交互。这种设计提供了一种高效且用户友好的接口,使得设备管理变得更加简单和灵活。
kernel Object
在Linux内核架构中,kobject扮演着一个核心角色,它是一个用于抽象化内核对象的框架。kobject不仅构成了内核中各种子系统的基础构件,而且能够代表设备、驱动程序、总线等多样化的内核实体。
kobject架构提供了一种灵活且统一的模型,用以维护和管理内核对象。每个Kobject实例都拥有一个独一无二的标识符和指向其上层Kobject的链接,这样的设计允许它们形成一个有序的层级网络。更进一步,Kobject可以附加多种属性,这些属性反映了对象的特征或状态,并且可以通过sysfs这一虚拟文件系统对外公布,使得用户空间程序能够访问和操作这些属性。
Sysfs作为Kobject信息呈现的媒介,将内核内部的设备和驱动程序等对象的状态和信息以文件的形式展现给用户空间。每当内核中新增设备或驱动程序时,相应的Kobject实例也会被动态创建,并通过Sysfs将这些信息映射到用户空间可访问的路径下。
在Linux内核的源码树中,struct kobject这一数据结构在"linux/kobject.h"头文件里定义。它经常作为其他结构体的成员出现,使得这些结构体所代表的内核对象能够整合进Kobject的管理体系中。通过这种方式,内核开发者可以轻松地为各种内核对象实现统一的管理和访问接口。
struct kobject { const char*name; struct list_headentry; struct kobject*parent; struct kset*kset; struct kobj_type*ktype; struct kernfs_node*sd; /* sysfs directory entry */ struct krefkref; ... };
其中:- kref:提供kobject的引用技术。- ktype:kobject关联的类型。- kset:指向同一类kobject集合的指针。- sd:当前kobject在/sys下的目录条目。
sysfs使用方式
Linux内核中使用sysfs的步骤比较简单:(1)在sys路径下创建目录;(2)创建sysfs文件。下面将详细展开这两步涉及的内核API。
在sys下创建目录
struct kobject * kobject_create_and_add ( const char * name, struct kobject * parent);
Linux内核预定义了几个常用的parent参数:- kernel_kobj:在/sys/kernel下创建目录;- firmware_kobj:在/sys/firmware下创建目录;- fs_kobj:在/sys/fs下创建目录。- 如果parent取值为NULL,则在/sys下面创建目录。
相应地,如果需要删除对应的sysfs目录,可以用:
void kobject_put(struct kobject *kobj);
创建sysfs文件
sysfs文件可以通过sysfs属性来创建,它定义在头文件"sysfs.h"中:
struct kobj_attribute { struct attribute attr; ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr, char *buf); ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count); };
attr表示将要创建的文件(属性),而show和store分别表示对应的sysfs文件在读和写操作时的回调函数。
struct kobj_attribute可以通过__ATTR宏来创建:
__ATTR(name, permission, show_ptr, store_ptr);
准备好attr之后,可以通过sysfs_create_file来创建出sysfs文件:
int sysfs_create_file ( struct kobject * kobj, const struct attribute * attr);
如果需要删除对应的sysfs文件,可以用:
void sysfs_remove_file ( struct kobject * kobj, const struct attribute * attr);
sysfs创建设备节点
前面描述了如何创建一个基本的sysfs,接下载描述的是如何创建设备节点的sysfs。
实际创建设备节点的sysf跟基本的sysfs是一样的,只是结构体换了一个名字。它使用DEVICE_ATTR宏,可以定义一个struct device_attribute设备属性,使用sysfs_create_file便可以在设备目录下创建具有show和store方法的节点。
DEVICE_ATTR宏定义
DEVICE_ATTR宏定义如下:
#define DEVICE_ATTR(_name, _mode, _show, _store) struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
__ATTR宏定义,宏定义在include/linux/sysfs.h文件中,如下:
#define __ATTR(_name, _mode, _show, _store) { .attr = {.name = __stringify(_name), .mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, .show = _show, .store = _store, }
struct device_attribute结构体,该结构体的定义在include /linux/device.h,其定义如下:
struct device_attribute { struct attribute attr; ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf); ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); };
struct attribute结构体, 该结构体的定义在include /linux/device.h,其定义如下:
struct attribute { const char *name; umode_t mode; #ifdef CONFIG_DEBUG_LOCK_ALLOC bool ignore_lockdep:1; struct lock_class_key *key; struct lock_class_key skey; #endif };
DEVICE_ATTR宏定义等价说明
我们顶一个文件的范式如下
DEVICE_ATTR(_name, _mode, _show, _store)
等价于:
struct device_attribute dev_attr_##_name = { .attr = {.name = __stringify(_name), .mode = VERIFY_OCTAL_PERMISSIONS(_mode)}, .show = _show, .store = _store, }
show函数的详细描述:
ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf);
参数说明:
入参buf是需要我们填充的string即我们cat属性节点时要显示的内容;-函数的返回值是我们填充buf的长度,且长度应当小于一个页面的大小(4096字节);
其他参数一般不用关心。
实例说明,当我们使用cat命令的时候,将调用该函数
static ssize_t show_youyeetoo_device(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%sn", mybuf); }
store函数的详细描述:
ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
参数说明:
入参buf是用户传入的字符串,即echo到属性节点的内容;
入参count是buf中字符串的长度。
函数的返回值通常返回count即可。
其他参数一般不用关心。
实例说明,当我们使用echo命令的时候,将调用该函数:
static ssize_t store_youyeetoo_device(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { sprintf(mybuf, "%s", buf); return count; }
mode的权限定义,在kernel/include/uapi/linux/stat.h中:
#define S_IRWXU 00700 //用户可读写和执行 #define S_IRUSR 00400//用户可读 #define S_IWUSR 00200//用户可写 #define S_IXUSR 00100//用户可执行 #define S_IRWXG 00070//用户组可读写和执行 #define S_IRGRP 00040//用户组可读 #define S_IWGRP 00020//用户组可写 #define S_IXGRP 00010//用户组可执行 #define S_IRWXO 00007//其他可读写和执行 #define S_IROTH 00004//其他可读 #define S_IWOTH 00002//其他可写 #define S_IXOTH 00001//其他可执行
至此,我们已经定义好了.show和.store函数,那么就可以使用DEVICE_ATTR了。
static DEVICE_ATTR(youyeetoo_device, S_IWUSR | S_IRUSR, show_youyeetoo_device, store_youyeetoo_device);
device attribute添加到sysfs
上面描述的是sysfs的读写接口的定义方式,他还需要注册到sysfs中。才会在对应驱动中显示我们的文件,其中:
注册函数:sysfs_create_file();
注销函数:sysfs_remove_file();
样例代码代码:
注册样例:
sysfs_create_file(&(youyeetoo_dev->kobj), &dev_attr_youyeetoo.attr);
注销样例:
sysfs_remove_file(&(youyeetoo_dev->kobj), &dev_attr_youyeetoo.attr);
sysfs样例测试
完整的测试代码,在sysfs的驱动节点里面生成一个youyeetoo文件,文件支持读写功能。
#include #include #include #include #include static int major; static struct class *youyeetoo_class; struct device *youyeetoo_dev; static char youyeetoo_buff[100] = "youyeetoo"; static ssize_t show_youyeetoo_device(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%sn", youyeetoo_buff); } static ssize_t store_youyeetoo_device(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { sprintf(youyeetoo_buff, "%s", buf); return count; } static DEVICE_ATTR(youyeetoo, S_IWUSR | S_IRUSR, show_youyeetoo_device, store_youyeetoo_device); struct file_operations youyeetoo_ops = { .owner = THIS_MODULE, }; static int youyeetoo_init(void) { major = register_chrdev(0, "youyeetoo", &youyeetoo_ops); youyeetoo_class = class_create(THIS_MODULE, "youyeetoo"); youyeetoo_dev = device_create(youyeetoo_class, 0, MKDEV(major, 0), NULL, "youyeetoo"); if (sysfs_create_file(&(youyeetoo_dev->kobj), &dev_attr_youyeetoo.attr)) { //在mytest_device设备目录下创建一个sys_device_file属性文件 return -1; } return 0; } static void youyeetoo_exit(void) { device_destroy(youyeetoo_class, MKDEV(major, 0)); class_destroy(youyeetoo_class); unregister_chrdev(major, "youyeetoo"); sysfs_remove_file(&(youyeetoo_dev->kobj), &dev_attr_youyeetoo.attr); } module_init(youyeetoo_init); module_exit(youyeetoo_exit); MODULE_AUTHOR("youyeetoo"); MODULE_LICENSE("GPL");
将上面驱动编译烧录之后,就可以sysfs下看到我们创建的文件:
测试我们创建的sysfs
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !