写点什么

Linux 设备驱动系列 (11) —— sysfs 文件系统

  • 2024-05-04
    浙江
  • 本文字数:3233 字

    阅读完需:约 11 分钟

Linux设备驱动系列(11) —— sysfs文件系统

关注微信公众号:Linux 内核拾遗

文章来源:https://mp.weixin.qq.com/s/hS-IZzBn6L09lAllpBBC7w


Linux 内核提供了多种用户态和内核态的通信机制,本文将重点介绍 sysfs 文件系统。其他的通信机制可以参考前文:


Linux设备驱动系列(八)——ioctl系统调用


Linux设备驱动系列(九)——procfs文件系统

1 sysfs 文件系统介绍

Sysfs(System Filesystem)是 Linux 内核提供的一种虚拟文件系统,用于向用户空间公开有关设备和驱动程序的信息。它类似于/proc 文件系统,但是专注于设备和驱动程序信息,而非进程信息。


Sysfs 通过文件和目录的方式组织信息,其中每个文件或目录对应于系统中的一个设备、驱动程序或者其他内核对象。这些文件通常包含有关设备和驱动程序的属性、状态和配置信息。有些文件可以被写入,用于配置和控制设备。


Sysfs 通常被挂载在/sys 目录下,它提供了一种方便的方式,让用户空间的程序可以动态地获取和管理系统中的设备信息,而无需直接访问内核数据结构。

2 Kernel Object

在 Linux 内核中,Kobject 是内核对象(Kernel Object)的抽象表示。它是内核中几乎所有子系统的基础,用于表示这些子系统中的各种对象,如设备、驱动程序、总线等。


Kobject 提供了一个通用的、层次化的对象模型,用于管理内核中的各种对象。每个 Kobject 都有一个唯一的名称和一个指向其父 Kobject 的指针,从而组织成一个层次结构。此外,Kobject 还可以拥有属性(如设备属性、驱动程序属性等),这些属性可以通过 sysfs 文件系统向用户空间公开。


Sysfs 是通过 Kobject 来表示设备、驱动程序等内核对象的信息的一种方式。当内核中的设备、驱动程序等对象被创建时,相应的 Kobject 也会被创建,然后通过 Sysfs 将其信息暴露给用户空间。


struct kobject 在头文件"linux/kobject.h"中定义,它通常被嵌入到其他结构体中。


struct kobject {    const char    *name;    struct list_head  entry;    struct kobject    *parent;    struct kset    *kset;    struct kobj_type  *ktype;    struct kernfs_node  *sd; /* sysfs directory entry */    struct kref    kref;    ...};
复制代码


其中:


  • kref:提供 kobject 的引用技术。

  • ktype:kobject 关联的类型。

  • kset:指向同一类 kobject 集合的指针。

  • sd:当前 kobject 在/sys 下的目录条目。

3 Sysfs 使用方式

Linux 内核中使用 sysfs 的步骤比较简单:(1)在/sys 路径下创建目录;(2)创建 sysfs 文件。下面将详细展开这两步涉及的内核 API。

3.1 在/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);
复制代码

3.2 创建 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);
复制代码


将前面介绍的 API 汇总起来,创建一个 sysfs 文件的过程代码如下:


struct kobj_attribute my_attr = __ATTR(my_value, 0660, sysfs_show, sysfs_store);static ssize_t sysfs_show(struct kobject *kobj,                          struct kobj_attribute *attr, char *buf){    return sprintf(buf, "%d", my_value);}static ssize_t sysfs_store(struct kobject *kobj,                            struct kobj_attribute *attr,const char *buf, size_t count){        sscanf(buf,"%d",&my_value);        return count;}
/*Creating a directory in /sys/kernel/ */kobj_ref = kobject_create_and_add("my_sysfs", kernel_kobj);/*Creating sysfs file for my_value*/if(sysfs_create_file(kobj_ref,&my_attr.attr)){ printk(KERN_INFO"Failed to create sysfs file\n"); goto r_sysfs;}kobject_put(kobj_ref); sysfs_remove_file(kernel_kobj, &my_attr.attr);
复制代码

4 Sysfs 完整代码演示

kernel_driver.c


#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/kdev_t.h>#include <linux/fs.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/slab.h>#include <linux/uaccess.h>#include <linux/kthread.h>#include <linux/wait.h>#include <linux/err.h>
uint32_t read_count = 0;static struct task_struct *wait_thread;
dev_t dev = 0;static struct class *dev_class;static struct cdev my_cdev;wait_queue_head_t my_waitqueue;int waitqueue_flag = 0;
static int wait_function(void *unused){
while (1) { pr_info("Waiting For Event...\n"); wait_event_interruptible(my_waitqueue, waitqueue_flag != 0); if (waitqueue_flag == 2) { pr_info("Event Came From Exit Function\n"); return 0; } pr_info("Event Came From Read Function - %d\n", ++read_count); waitqueue_flag = 0; } do_exit(0); return 0;}
static ssize_t my_read(struct file *filp, char __user *buf, size_t len, loff_t *off){ waitqueue_flag = 1; wake_up_interruptible(&my_waitqueue); return 0;}
static struct file_operations fops = { .owner = THIS_MODULE, .read = my_read,};
static int __init my_driver_init(void){ if ((alloc_chrdev_region(&dev, 0, 1, "my_dev")) < 0) return -1;
cdev_init(&my_cdev, &fops); my_cdev.owner = THIS_MODULE; my_cdev.ops = &fops;
if ((cdev_add(&my_cdev, dev, 1)) < 0) goto r_class;
if (IS_ERR(dev_class = class_create(THIS_MODULE, "my_class"))) goto r_class;
if (IS_ERR(device_create(dev_class, NULL, dev, NULL, "my_device"))) goto r_device;
init_waitqueue_head(&my_waitqueue);
if ((wait_thread = kthread_create(wait_function, NULL, "WaitThread"))) wake_up_process(wait_thread);
return 0;
r_device: class_destroy(dev_class);r_class: unregister_chrdev_region(dev, 1); return -1;}
static void __exit my_driver_exit(void){ waitqueue_flag = 2; wake_up_interruptible(&my_waitqueue); device_destroy(dev_class, dev); class_destroy(dev_class); cdev_del(&my_cdev); unregister_chrdev_region(dev, 1);}
module_init(my_driver_init);module_exit(my_driver_exit);
MODULE_LICENSE("GPL");MODULE_AUTHOR("feifei <feifei@gmail.com>");MODULE_DESCRIPTION("Simple linux driver");MODULE_VERSION("1.7");
复制代码


编译运行演示如下:



关注微信公众号:Linux 内核拾遗

文章来源:https://mp.weixin.qq.com/s/hS-IZzBn6L09lAllpBBC7w


发布于: 刚刚阅读数: 3
用户头像

聚沙成塔 2023-01-12 加入

分享Linux内核开发相关的编程语言、开发调试工具链、计算机组成及操作系统内核知识、Linux社区最新资讯等

评论

发布
暂无评论
Linux设备驱动系列(11) —— sysfs文件系统_C语言_Linux内核拾遗_InfoQ写作社区