Linux内核中关于虚拟文件系统(sysfs)一文翻译

本文是对Documentation\filesystems\sysfs.txt文章的翻译;内核版本是:linux-3.4.2。

sysfs:此文件系统用于导出内核中的kobject对象。
sysfs是一种基于RAM的文件系统;它提供了一种导出内核数据结构及其属性到用户空间的方法,使这些数据结构可以和用户空间建立联系。

sysfs和内核中的kobject结构体有紧密的联系。有关kobject结构的更多信息,请阅读Documentation/kobject.txt一文。

使用sysfs:

如果需要将sysfs相关的代码编译进去,你需要开启CONFIG_SYSFS宏;您可以通过以下方式访问它:

mount -t sysfs sysfs /sys

创建目录:

对于在系统中注册的每个kobject,都会在sysfs中为其创建一个目录。该目录被创建为kobject父级的子目录,并将内部对象层次结构显示到用户空间。sysfs中的顶级目录表示对象层次结构的共同根目录;即对象所属的子系统。

sysfs内部存储一个指向kobject的指针,该指针实现与目录关联的sysfs_dirent对象中的目录。在过去,这个kobject指针已经被sysfs用来在文件被打开或关闭时直接对kobject进行引用计数。现在的sysfs实现中,kobject引用计数只能由函数sysfs_schedule_callback()函数直接修改。

attributes(属性):

kobjects的属性(attributes)可以以文件系统中常规文件的形式导出。sysfs将对文件I/O操作转发给为属性定义的方法,提供读取和写入内核属性的方法。属性应该是ASCII文本文件,最好每个文件只有一个值。值得注意的是,每个文件只包含一个值可能效率不高,因此表达相同类型值的数组是可以接受的。

属性(attribute)定义很简单:

struct attribute {
	char            * name;
	struct module	*owner;
	umode_t         mode;
};

操作函数如下:

int sysfs_create_file(struct kobject * kobj, const struct attribute * attr);
void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr);

裸属性不包含读取或写入属性值的方法。鼓励子系统定义它们自己的属性结构和包装函数,以便为特定对象类型添加和删除属性。

例如,驱动程序模型定义了struct device_attribute,如下所示,关于device的属性定义以及添加/移除的方法:

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);	 
};
/*相关操作函数 */
int device_create_file(struct device *, const struct device_attribute *);
void device_remove_file(struct device *, const struct device_attribute *);

它还定义了下面的宏来定义设备(device)属性:

#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,
	},
};
sysfs子系统相关的回调函数:
当子系统定义新的属性类型时,它必须实现一组sysfs操作,以将读写调用转发给属性所有者的显示和存储方法。
struct sysfs_ops {
	ssize_t (*show)(struct kobject *, struct attribute *, char *);
	ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
};
子系统应该已经定义了一个struct kobj_type作为这种类型的描述符,这是sysfs_ops指针的存储位置。有关更多信息,请参阅kobject文档。
读取或写入文件时,sysfs将调用该类型的适当方法。该方法然后将泛型结构kobject和结构属性指针转换为适当的指针类型,并调用相关的方法。

示例:

#define to_dev(obj) container_of(obj, struct device, kobj)
#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)                          
{
	struct device_attribute *dev_attr = to_dev_attr(attr);
	struct device *dev = to_dev(kobj);
	ssize_t ret = -EIO;

	if (dev_attr->show)
		ret = dev_attr->show(dev, dev_attr, buf);
	if (ret >= (ssize_t)PAGE_SIZE) {
		print_symbol("dev_attr_show: %s returned bad count\n",(unsigned long)dev_attr->show);			
	}
	return ret;
}
读/写属性数据:

要读取或写入属性,必须在声明属性时指定show()或store()方法。方法类型应该像为设备属性定义的一样简单:

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);
函数的应该包含一个对象,一个属性和一个缓冲区作为参数。
sysfs分配一个大小为(PAGE_SIZE)的缓冲区并将其传递给该函数。每次读或写时,sysfs都会调用该方法一次。这在方法实现上强制执行以下行为:

  • 在读取时,show()方法应该填充整个缓冲区。回想一下,一个属性只应该导出一个值或者一个相似值的数组,所以这不应该太麻烦。这允许用户空间随意地在整个文件上任意地进行部分读取。
  • 在写入时,sysfs需要在第一次写入时传递整个缓冲区。sysfs然后将整个缓冲区内容传递给store()方法。当写sysfs文件时,用户空间进程应该首先读取整个文件,修改它希望改变的值,然后写回整个缓冲区。读取和写入数值时,属性方法的实现应该在相同的缓冲区上运行。

其他说明:

  • 无论当前文件位置如何,写入都会导致show()方法被重新加载。
  • 缓冲区的长度始终为PAGE_SIZE(4096)个字节。
  • show()方法应该返回打印到缓冲区的字节数。这是scnprintf()函数的返回值。
  • show()应该总是使用scnprintf()函数。
  • store()函数应该返回缓冲区中使用的字节数。如果整个缓冲区已被使用,只需返回count参数。
  • show()或store()函数可能返回错误。如果出现错误值,请确保返回错误值。
  • 传递给方法的kobject对象将通过引用计数其嵌入对象的sysfs固定在内存中。然而,对象所代表的物理实体(例如设备)可能不存在。如有必要,一定要有办法检查这一点。

设备(device)属性的一个非常简单的实现如下所示:

static ssize_t show_name(struct device *dev, struct device_attribute *attr,char *buf)                     
{
    return scnprintf(buf, PAGE_SIZE, "%s\n", dev->name);
}

static ssize_t store_name(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)                   
{
    snprintf(dev->name, sizeof(dev->name), "%.*s",(int)min(count, sizeof(dev->name) - 1), buf);    
    return count;
}

static DEVICE_ATTR(name, S_IRUGO, show_name, store_name);

请注意,真正的实现不允许用户空间为设备设置名称。

sysfs顶级目录:
sysfs目录的安排显示了内核数据结构的关系。
sysfs的顶级目录如下所示:
block/
bus/
class/
dev/
devices/
firmware/
net/
fs/
devices/包含设备树的文件系统表示。它直接映射到内部内核设备树,这是struct device的层次结构。
bus/包含内核中各种总线类型的平面目录布局。每个总线的目录包含两个子目录:
devices/
drivers/
devices/包含系统中发现的指向root/下的设备目录的每个设备的符号链接。
drivers/包含为特定总线上的设备加载的每个设备驱动程序的目录。

fs/包含一些文件系统的目录。目前,每个希望导出属性的文件系统都必须在fs/下创建自己的层次结构。

dev/包含两个目录,分别是char/和block/。在这两个目录中有符号链接名称为<major>:<minor>。这些符号链接指向给定设备的sysfs目录。关于设备模型的更多信息可以查看Documentation/driver-model/目录。

sysfs中目前存在以下接口层:
关于devices的接口((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);		 
};

声明:

DEVICE_ATTR(_name, _mode, _show, _store);

创建/删除方法:

int device_create_file(struct device *dev, const struct device_attribute * attr);
void device_remove_file(struct device *dev, const struct device_attribute * attr);
关于bus驱动的接口((include/linux/device.h):
结构定义:
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);
};

声明:

BUS_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 *);
关于device drivers的接口(include/linux/device.h):
结构定义:
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);                     
};

声明:

DRIVER_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 *);

备注:

sysfs目录结构和每个目录中的属性定义了内核和用户空间之间的ABI。至于任何ABI,重要的是这个ABI是稳定和适当的文件。所有新的sysfs属性必须记录在Documentation/ABI文件中。有关更多信息,另请参阅Documentation/ABI/README。

猜你喜欢

转载自blog.csdn.net/caihaitao2000/article/details/80641546