Linux sysfs学习

1.概述

介绍sysfs文件系统,学习如何创建sysfs结点,学习linux设备模型

2.简介

Sysfs文件系统时linux众多文件系统中的一个,在linux中,每一个文件系统都有其特殊用途,如ext2用于快速读写存储文件;ext3用于记录日志等。

Linux设备驱动模型由大量的数据结构和算法组成。这些数据结构之间关系非常复杂,多个数据结构之间通过指针相互关联,构成树状或网状关系。显示这种关系的最好方法是利用一种树形的文件系统,但是这种文件系统需要具有其他文件系统没有的功能,例如显示内核中关于设备、驱动、总线的信息。为了这一目的,Linux内核开发者创建了sysfs文件系统。
Sysfs文件系统是linux2.6内核引入的一个新特性,其只是一个只存在于内存中的文件系统。内核通过这个文件系统将信息导出到用户空间中。Sysfs文件系统的目录之间的关系非常复杂,各目录与文件之间既有树形关系,又有目录关系。
在内核中,这种关系主要由设备驱动模型来表示。在sysfs文件系统中产生的文件大多数是ASCII文件,通常每个文件由一个值,也可以叫属性文件。

2.1.Sysfs文件系统与内核结构关系

Sysfs文件系统是内核对象kobject、属性kobj_type及它们相互关系的一种表现机制。用户可以从sysfs文件系统中读出内核数据,也可以将用户空间的数据写入内核。这样,我们就可以在用户空间通sysfs文件系统设置驱动程序的状态和属性,下表表示内核数据结构和sysfs文件系统的关系。

Linux内核数据结构Sysfs中的结构
kobject目录
Kobj_type属性文件
对象之间关系符号链接

2.2.Sysfs文件系统的目录结构

Sysfs文件系统与其他文件系统一样,由目录、文件和链接组成。与其他文件系统不同的是,sysfs文件系统表示的内容与其他文件系统的内容不一样,另外,sysfs文件系统只存在于内存中,动态表示这内核的数据结构。Sysfs文件系统挂接着一些子目录,这些目录代表注册了sysfs中的主要的子系统,主要如下:
在这里插入图片描述
当设备启动时,设备驱动模型会注册kobject对象,并在sysfs文件系统中产生以上的目录,以下是对一些主要目录的说明。

2.2.1.Block目录

块目录包含了在系统中的发现的每个块设备的子目录,每个块设备对应一个子目录。每个块设备的目录中有各种属性,描述了设备的各种信息,如设备的大小、设备号
等。块设备目录下有一个表示I/O调度器的目录,这个目录提供了一些属性文件,用户和管理员可以通过调整它们来优化性能。

2.2.2.Bus目录

总线目录包含了在内核中注册而得到支持的每个物理总线的子目录,如usb、pci、i2c总线等。Bus目录的结构如下:
在这里插入图片描述
我们可以看到bus目录下存在许多注册到系统的总线,其中每个目录的结构都差不多,以i2c为例,查看其目录结构如下:
在这里插入图片描述
该目录包含了devices和drivers目录。Devices目录包含了i2c总线下所有的设备的列表,其实是指向设备目录中相应设备的符号链接。Drivers目录包含了i2c总线下注册的所有驱动程序目录。每个驱动目录中有允许查看和操作设备参数的属性文件,还有指向该设备所绑定的物理设备的符号链接。

2.2.3.Class目录

类目录中的子目录表示每一个注册到内核中的设备类,例如混杂设备类(misc)、固件类(firmware)、图形类(graphics)等。类对象只包含一些设备的总称,例如网络设备类包含了一切的网络设备,集中在sys/class/net目录下。输入设备类包含了一切的输入设备,如鼠标、键盘和触摸板等,它们集中在sys/class/input目录下。Class目录结构如下:
在这里插入图片描述

3.设备驱动模型

设备驱动模型由几个核心数据结构组成,分别是kobject、kset和subsystem。这些结构使设备驱动模型组成了一个层次结构,该层次结构将驱动、总线和设备等联系起来,形成一个完整的设备模型。下面对这些结构进行介绍。

3.1.struct kobject

从宏观角度来说,设备驱动模型是一个设备和驱动组成的层次结构。例如一条总线上挂接了很多设备,总线在Linux中也是一直特殊设备,假设总线A,在A上挂接了USB控制器B,在B上挂接了设备C和D,另外,A上还挂接了E、F设备,则形成下列关系:
在这里插入图片描述
在sysfs文件系统中,这些设备使用树形目录来表示,如下所示:
在这里插入图片描述
树形结构中每一个目录与一个kobject对象相对应,其包含了目录的组织结构和名字等信息。在linux系统中,kobject结构是组成设备驱动模型的基本结构。Kobject结构定义如下:

struct kobject {
	const char		*name;		//kobject 的名称
	struct list_head	entry;		//连接下一个kobject结构
	struct kobject		*parent;		//指向父kobject结构
	struct kset		*kset;		//指向kset集合
	struct kobj_type	*ktype;		//指向kobject的类型描述
	struct sysfs_dirent	*sd;			//对应sysfs的文件目录
	struct kref		kref;			//kobject引用计数
	unsigned int state_initialized:1;	//kobject初始化标志位
	unsigned int state_in_sysfs:1;	//是否加入sysfs中
	unsigned int state_add_uevent_sent:1;
	unsigned int state_remove_uevent_sent:1;
	unsigned int uevent_suppress:1;
};
3.2.操作kobject的函数
3.2.1.Kobject_init

函数原型:void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
功能:初始化kobject结构
参数:
kobj:指向要初始化的kobject结构
ktype:指向用于初始化kobject成员ktype的kobj_type结构

3.2.2.kobject_init_internal

函数原型:void kobject_init_internal(struct kobject *kobj)
功能:初始化kobject结构内部成员
参数:
kobj:指向要初始化的kobject结构

3.2.3.kobject_get

函数原型:struct kobject *kobject_get(struct kobject *kobj)
功能:增加kobject的引用计数
参数:
kobj:指向要增加引用计数的kobject结构
返回值:
成功:返回指向kobject的指针 失败:返回NULL

3.2.4.kobject_put

函数原型:void kobject_put(struct kobject *kobj)
功能:减少kobject的引用计数
参数:
kobj:指向要减少引用计数的kobject结构,当引用计数为0是时,释放该对象及其资源。

3.2.5.kobject_rename

函数原型:int kobject_rename(struct kobject *kobj, const char *new_name)
功能:重新命名kobject结构的名字
参数:
kobj:指向要设置名字的kobject结构 new_name:设置的名字
返回值:
成功:返回0 失败:返回错误码

3.2.6.kobject_set_name

函数原型:int kobject_set_name(struct kobject *kobj, const char *fmt, …)
功能:设置kobject结构的名字
参数:
kobj:指向要设置名字的kobject结构 fmt:用来格式化名字的字符串
返回值:
成功:返回0 失败:返回错误码

3.3.Struct kobj_type

每个kobject对象都有一些属性,这些属性由kobj_type结构表示,在kobject结构中有指向kobj_type的指针,其关系如下图所示:
在这里插入图片描述
其中,kobj_type定义如下:

struct kobj_type {
	void (*release)(struct kobject *kobj);		//kobject对象释放函数
	const struct sysfs_ops *sysfs_ops;
	struct attribute **default_attrs;
	const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
	const void *(*namespace)(struct kobject *kobj);
};

kobj_type的default_attrs成员保存了属性数组,每个kobject对象可以有一个或多个属性。属性结构定义如下:

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
};

内核为了方便我们使用,向我们提供了辅助的宏:

#define __ATTR(_name,_mode,_show,_store) { 				\
	.attr = {.name = __stringify(_name), .mode = _mode },		\
	.show	= _show,						\
	.store	= _store,						\
}

kobj_type的sysfs_ops成员表示了对这些属性的操作方法,sysfs_ops定义如下:

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 *);
};
3.4.非默认属性

在许多情况下,kobject类型的default_attrs成员定义了kobject拥有的所有默认属性,但在特殊情况下,也可以对kobjectt添加一些非默认属性,用来控制kobject代表的总线、设备、驱动的行为,以下是添加非默认属性的函数的介绍。

3.4.1.Sysfs_create_file

函数原型:int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
功能:给指定的kobject创建非默认属性
参数:
Kobj:指向要添加属性的kobject
Attr:指向要添加的属性
返回值:成功,返回0 失败:返回错误码

3.4.2.sysfs_remove_file

函数原型:int sysfs_remove_file(struct kobject * kobj, const struct attribute * attr)
功能:给指定的kobject删除属性
参数:
Kobj:指向要删除属性的kobject
Attr:指向要删除的属性

3.4.3.sysfs_chmod_file

函数原型:int sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr,
umode_t mode)
功能:给指定的kobject修改属性权限
参数:
Kobj:指向要修改属性权限的kobject
Attr:指向要修改的属性
Mode:表示权限
更多函数的用法可以查看include/linux/sysfs.h文件

3.5.内核常见的属性结构

一个单独的属性结构并没有包含读写属性值得方法。因此在内核许多子系统中就为增删特定对象类型的属性定义自己的属性结构体和封装函数。以下的接口层普遍存在于sysfs中,常见的有以下结构:

3.5.1.Device_attribute

设备属性的定义如下所示:

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);
};

为了方便定义设备属性,内核向我们提供了辅助的宏:

#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

例如,声明以下属性

static DEVICE_ATTR(foo, S_IWUSR | S_IRUGO, show_foo, store_foo);

就等同于如下代码:

static struct device_attribute dev_attr_foo = {
       .attr	= {
		.name = "foo",
		.mode = S_IWUSR | S_IRUGO,
		.show = show_foo,
		.store = store_foo,
	},
};

增加或删减属性用以下函数:

int device_create_file(struct device *dev, const struct device_attribute * attr);
void device_remove_file(struct device *dev, const struct device_attribute * attr);
3.5.2.bus_attribute

对于新增的总线,我们使用bus_attribute定义总线的属性,bus_attribute的定义如下所示:

struct bus_attribute {
        struct attribute        attr;
        ssize_t (*show)(struct bus_type *, char * buf);
        ssize_t (*store)(struct bus_type *, const char * buf, size_t count);
};

同样,内核也想我们提供的辅助宏来方便我们定义总线属性,如下所示:

#define BUS_ATTR(_name, _mode, _show, _store)	\
	struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)

增加或删减总线属性用以下函数:

int bus_create_file(struct bus_type *, struct bus_attribute *);
void bus_remove_file(struct bus_type *, struct bus_attribute *);
3.5.3.driver_attribute

对于驱动,内核向我们提供了driver_attribute来定义驱动的属性,driver_attribute的定义如下:

struct driver_attribute {
        struct attribute        attr;
        ssize_t (*show)(struct device_driver *, char * buf);
        ssize_t (*store)(struct device_driver *, const char * buf,
                         size_t count);
};

同样,内核也想我们提供的辅助宏来方便我们定义总线属性,如下所示

#define DRIVER_ATTR(_name, _mode, _show, _store) \
	struct driver_attribute driver_attr_##_name = __ATTR(_name, _mode, _show, _store)

增加或删减驱动属性用以下函数:

int driver_create_file(struct device_driver *, const struct driver_attribute *);
void driver_remove_file(struct device_driver *, const struct driver_attribute *);
3.5.4.class_attribute

对于类,内核向我们提供了class_attribute来定义类的属性,class_attribute的定义如下所示:

struct class_attribute {
	struct attribute attr;
	ssize_t (*show)(struct class *class, struct class_attribute *attr,
			char *buf);
	ssize_t (*store)(struct class *class, struct class_attribute *attr,
			const char *buf, size_t count);
	const void *(*namespace)(struct class *class,
				 const struct class_attribute *attr);
};

同样,内核也想我们提供的辅助宏来方便我们定义类属性,如下所示:

#define CLASS_ATTR(_name, _mode, _show, _store) \
	struct class_attribute class_attr_##_name = __ATTR(_name, _mode, _show, _store)

增加或删减类属性用以下函数:

int class_create_file(struct class *class,const struct class_attribute *attr);
void class_remove_file(struct class *class,const struct class_attribute *attr);
3.6.设备驱动模型结构

在linux设备驱动模型中,设备驱动模型在内核中的关系用kobject结构来表示,在用户空间的关系用sysfs文件系统的结构来表示。如下图所示,左边是bus子系统在内核中的关系,通过kobject结构来组织。右边是sysfs文件系统的结构关系,使用目录和文件来表示。左边的kobject和右边的目录或文件的一一对应的关系,如果左边有一个kobject对象,则右边就对应一个目录。目录下的文件表示该kobject的属性。
在这里插入图片描述

3.6.1.Kset集合

Kobject通过kset组织成层次化的结构。Kset是具有相同类型的kobject的集合,像驱动程序一样放在/sys/drivers/目录下,目录drivers是一个kset对象,包含系统中的驱动程序对应的目录,驱动程序的目录由kobject表示。Kset结构定义如下:

struct kset {
	struct list_head list;
	spinlock_t list_lock;
	struct kobject kobj;
	const struct kset_uevent_ops *uevent_ops;		//热插拔事件函数集
};

其中uevent_ops定义如下:

struct kset_uevent_ops {
	int (* const filter)(struct kset *kset, struct kobject *kobj);		//过滤
	const char *(* const name)(struct kset *kset, struct kobject *kobj);
	int (* const uevent)(struct kset *kset, struct kobject *kobj,
		      struct kobj_uevent_env *env);
};

一个热插拔事件是从内核空间发送到用户空间的通知,表明系统某些部分的配置已经发生变化。用户空间收到内核空间的通知后,会调用相应的程序,处理配置的变化。例如,当U盘插入到usb系统时,会产生一个热插拔事件,内核会捕获这个热插拔事件,并调用用户空间的/sbin/hotplug程序,该程序通过加载驱动程序来响应U盘设备插入的动作。当驱动程序将kobject注册到设备驱动模型时,就会产生热插拔事件,即内核调用kobject_add() 和kobject_del()函数时,会产生热插拔事件。热插拔事件产生时,内核会根据kobject的kset指针找到所属的kset结构体,执行kset结构中的uevent_ops包含的热插拔函数。在设备初始化时,需要内核提供相关信息,此时,可以通过uevent_ops中的uevent函数在环境变量中传递。

3.6.2.Kset与kobject关系

Kset是kobject的一个集合,用来与kobject建立层次关系。内核将相似的kobject结构链接在kset集合,这些相似的kobject可能有相似的属性,使用统一的kset来表示。下图表示kset集合与kobject之间关系。
在这里插入图片描述

3.6.3.Kset操作函数
3.6.3.1.Kset_init

函数原型:void kset_init(struct kset *kset)
功能:初始化kset对象成员
参数:
kset:指向要初始化的kset结构

3.6.3.2.Kset_register

函数原型:int kset_register(struct kset *kset)
功能:注册kset结构
参数:
kset:指向要注册的kset结构

3.6.3.3.kset_unregister

函数原型:void kset_unregister(struct kset *kset)
功能:注销kset结构
参数:
kset:指向要注销的kset结构

3.6.3.4.引用计数相关

函数原型:struct kset *kset_get(struct kset *k)/void kset_put(struct kset *k)
功能:增加/减少kset引用计数
参数:
k:指向要增加/减少的kset结构

3.6.3.5.kobject_init_and_add

函数原型:int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
struct kobject *parent, const char *fmt, …)
功能:初始化和添加kobject到内核
参数:
kobj:指向要初始化的kobject结构
ktype:指向要与kobj关联的kobj_type结构
parent:指向kobj的父kobject结构
fmt:格式化参数

3.6.3.6.kobject_add_varg

函数原型:int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
const char *fmt, va_list vargs)
功能:把kobject添加加到驱动设备模型
参数:
kobj:指向要加入设备驱动模型的kobject结构
parent:指向kobj的父kobject结构,为NUL时,在sys下创建目录
fmt,vargs:格式化参数,与printf函数参数一样。

3.6.3.7.kobject_add_internal

函数原型:int kobject_add_internal(struct kobject *kobj)
功能:把kobject添加加到驱动设备模型,并在sysfs文件系统中创建一个目录
参数:
kobj:指向要加入设备驱动模型的kobject结构

3.6.3.8.kobject_del

函数原型:void kobject_del(struct kobject *kobj)
功能:从设备驱动模型中删除一个kobject添对象
参数:
kobj:指向删除kobject结构

4.示例代码

#include <linux/device.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sysfs.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/stat.h>
void sysfs_test_release(struct kobject *kobj);
ssize_t	sysfs_test_show(struct kobject *, struct attribute *,char *);
ssize_t	sysfs_test_store(struct kobject *,struct attribute *,const char *, size_t);

struct attribute sysfs_test_attrs = {
	.name = "sysfs_test_attrs",
	.mode = S_IRWXUGO,
};
static struct attribute *def_attrs [] = {
	&sysfs_test_attrs,
	NULL,
};
struct sysfs_ops sysfs_test_ops = {
	.show = sysfs_test_show,			//read func
	.store = sysfs_test_store,			//write func
};
struct kobj_type ktype = {
	.release = sysfs_test_release,
	.sysfs_ops = &sysfs_test_ops,
	.default_attrs = def_attrs,
};
void sysfs_test_release(struct kobject *kobj)
{
	printk(KERN_WARNING"sysfs release test!\n");
}

/*when cat sysfs_test,show the attribute name*/
ssize_t	sysfs_test_show(struct kobject *kobj, struct attribute *attr,char *buf)
{
	printk(KERN_WARNING"call sysfs_test_show()\n");
	printk(KERN_WARNING"attrname:%s\n",attr->name);
	sprintf(buf,"%s\n",attr->name);
	return strlen(attr->name)+2;
}
/*when echo xxx > sysfs_test,show it*/
ssize_t	sysfs_test_store(struct kobject *kobj,struct attribute *attr,const char *buf, size_t count)
{
	printk(KERN_WARNING"call sysfs_test_store()\n");
	printk("count:%d,write:%s",count,buf);
	return count;
}

struct kobject kobj;		/*the kobject to be added*/

static int sysfs_test_init(void)
{
	int ret = 0;
	printk(KERN_WARNING"init the kobj in sysfs_test_init()\n");
/*init the kobj and add to kernel*/
	ret = kobject_init_and_add(&kobj,&ktype,NULL,"sysfs_test");		
	if (ret !=0) {
		printk("init kobj fail!\n");
		return ret;
	}
	return ret;
}

void sysfs_test_exit(void)
{
	printk(KERN_WARNING"sysfs test exit\n");
	kobject_del(&kobj);
}

MODULE_LICENSE("GPL");

module_init(sysfs_test_init);
module_exit(sysfs_test_exit);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值