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

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

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

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

01

sysfs文件系统介绍

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

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

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

02

Kernel Object

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

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

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

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

structkobject{ constchar*name; structlist_headentry; structkobject*parent; structkset*kset; structkobj_type*ktype; structkernfs_node*sd;/* sysfs directory entry */ structkrefkref; ... };

其中:

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

  • ktype:kobject关联的类型。

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

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

03

Sysfs使用方式

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

3.1

在/sys下创建目录

structkobject * kobject_create_and_add ( constchar* name, structkobject * parent);

Linux内核预定义了几个常用的parent参数:

  1. kernel_kobj:在/sys/kernel下创建目录;

  2. firmware_kobj:在/sys/firmware下创建目录;

  3. fs_kobj:在/sys/fs下创建目录。

如果parent取值为NULL,则在/sys下面创建目录。

相应地,如果需要删除对应的sysfs目录,可以用:

voidkobject_put(structkobject *kobj);

3.2

创建sysfs文件

sysfs文件可以通过sysfs属性来创建,它定义在头文件"sysfs.h"中:

structkobj_attribute{ structattributeattr; ssize_t(*show)(struct kobject *kobj, struct kobj_attribute *attr, char*buf); ssize_t(*store)(struct kobject *kobj, struct kobj_attribute *attr, constchar*buf, size_tcount); };

其中attr表示将要创建的文件(属性),而show和store分别表示对应的sysfs文件在读和写操作时的回调函数。

struct kobj_attribute可以通过__ATTR宏来创建:

__ATTR(name, permission, show_ptr, store_ptr);

准备好attr之后,可以通过sysfs_create_file来创建出sysfs文件:

intsysfs_create_file(structkobject * kobj, conststructattribute * attr);

同样地,如果需要删除对应的sysfs文件,可以用:

voidsysfs_remove_file(structkobject * kobj, conststructattribute * attr);

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

structkobj_attribute my_attr = __ATTR(my_value, 0660, sysfs_show, sysfs_store); staticssize_t sysfs_show(structkobject *kobj, structkobj_attribute *attr, char*buf) { returnsprintf(buf, "%d", my_value); } staticssize_t sysfs_store(structkobject *kobj, structkobj_attribute *attr,constchar*buf, size_t count) { sscanf(buf,"%d",&my_value); returncount; }

/*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");gotor_sysfs;}kobject_put(kobj_ref); sysfs_remove_file(kernel_kobj, &my_attr.attr);

04

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_tread_count = 0;staticstructtask_struct*wait_thread;

dev_tdev = 0;staticstructclass*dev_class;staticstructcdevmy_cdev;wait_queue_head_tmy_waitqueue;intwaitqueue_flag = 0;

staticintwait_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");return0;}pr_info("Event Came From Read Function - %d\n", ++read_count);waitqueue_flag = 0;}do_exit(0);return0;}

staticssize_t my_read(struct file *filp, char__user *buf, size_tlen, loff_t*off){waitqueue_flag = 1;wake_up_interruptible(&my_waitqueue);return0;}

staticstructfile_operationsfops= {.owner = THIS_MODULE,.read = my_read,};

staticint__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)gotor_class;

if(IS_ERR(dev_class = class_create(THIS_MODULE, "my_class")))gotor_class;

if(IS_ERR(device_create(dev_class, NULL, dev, NULL, "my_device")))gotor_device;

init_waitqueue_head(&my_waitqueue);

if((wait_thread = kthread_create(wait_function, NULL, "WaitThread")))wake_up_process(wait_thread);

return0;

r_device:class_destroy(dev_class);r_class:unregister_chrdev_region(dev, 1);return-1;}

staticvoid__exitmy_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_DEION("Simple linux driver");MODULE_VERSION("1.7");

编译运行演示如下:

返回搜狐,查看更多

责任编辑:

平台声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。
阅读 ()