sysfs -用于导出内核对象(kobject)的文件系统
简介:
~~~~~~~~~~~
sysfs是一个最初基于ramfs的位于内存的文件系统。它提供一些方法以导出内核的数据结构、他们的属性和他们与用户空间的连接。sysfs始终与kobject的底层结构紧密相关。请阅读Documentation/kobject.txt文档以获得更多关于kobject接口的信息。
使用
~~~~~~~~~~~
sysfs通常被编译进内核。你可以通过使用以下命令访问它:
mount -t sysfs sysfs /sys
(此命令含义是挂载sysfs到根目录下的sys目录)
创建目录
~~~~~~~~~~~~~~~~~~
一旦有kobject在系统中注册,就会有一个目录在sysfs中被创建。 这个目录是作为kobject的parent下的子目录创建的,以准确的传递内核的对象层次到用户空间。sysfs中的顶层目录代表着内核对象层次的共同祖先;例如:某些对象属于某个子系统。 Sysfs内部存储着kobject,这些kobject在d_fsdata指针(在kobject的dentry结构体中)中拥有目录。 这使得sysfs可以在文件打开和关闭时,直接在kobject上实现引用计数。
属性
~~~~~~~~~~
kobject的属性能在文件系统中以普通文件的形式导出。Sysfs为属性定义了面向文件I/O操作的方法,以提供对内核属性的读写。属性应为ASCII码文本文件,以一个文件只存储一个属性值为宜。但一个文件只包含一个属性值可能影响效率, 所以一个包含相同数据类型的属性值数组也是被广泛接受的。混合类型、表达多行数据以及一些怪异的数据格式是会遭强烈反对。这样做是很丢脸的,而且你的代码会在未通知你的情况下被重写。
一个简单的属性结构定义如下:(到2.6.22.2已添加了struct module * owner;)
struct attribute {
char* name;
mode_tmode;
};
int sysfs_create_file(struct kobject * kobj, struct attribute * attr);
void sysfs_remove_file(struct kobject * kobj, struct attribute * attr);
一个裸的属性并不包含读写其属性值的方法。
最好为子系统定义自己的属性和为了增删特殊对象类型的属性而包装过的函数。例如:驱动程序模型定义的device_attribute结构体如下:
struct device_attribute {
struct attributeattr;
ssize_t (*show)(struct device * dev, char * buf);
ssize_t (*store)(struct device * dev, const char * buf);
};
int device_create_file(struct device *, struct device_attribute *);
void device_remove_file(struct device *, struct device_attribute *);
它为了定义设备的属性也定义了辅助的宏:
#define DEVICE_ATTR(_name, _mode, _show, _store)\
struct device_attribute dev_attr_##_name = {\
.attr = {.name=stringify(_name) , .mode= _mode },\
.show= _show,\
.store= _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操作必须被执行,以帮助读写函数实现属性所有者的显示和储存的方法。
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *, char *);
ssize_t (*store)(struct kobject *, struct attribute *, const char *);
};
[子系统应已经定义了一个kobj_type结构体作为这个类型的描述符,存储sysfs_ops的指针。更多的信息参见kobject的文档]
当一个文件被读写时,sysfs会为这个类型调用适当的方法。这个方法会将一般的kobject和attribute结构体指针 转换为适当的指针类型后调用相关联的函数。
示例:
#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
#define to_dev(d) container_of(d, struct device, kobj)
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 = 0;
if (dev_attr->show)
ret = dev_attr->show(dev, buf);
return ret;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
读写属性数据
在声明属性时,show()或store()方法必须被指明,以实现属性的读或写。这些方法的类型应该和以下的设备属性的定义一样简单。
ssize_t (*show)(struct device * dev, char * buf);
ssize_t (*store)(struct device * dev, const char * buf);
也就是说,他们应该只以一个处理对象和一个缓冲指针作为参数。sysfs会分配一个缓冲区的大小(PAGE_SIZE)并传递给这个方法。Sysfs将会为每次读写操作调用一次这个方法。这导致了这些方法的执行会出现以下的行为:implementations:
-在读方面,show()方法应该填充整个缓冲区。回想起属性应只导出了一个属性值或是一个同类型的属性值的数组,所以这个代价将不会不太高。这使得用户空间可以局部地读和任意的搜索整个文件。
-在些方面,sysfs希望在第一次写操作时得到整个缓冲区。之后Sysfs传递整个缓冲区给store()方法。
当要写sysfs文件时,用户空间进程应该首先读整个文件,修该想要改变的值,然后回写整个缓冲区。在读写属性值时,属性方法的执行应操作相同的缓冲区。
注记:
-缓冲区应总是PAGE_SIZE大小。对于i386,这个值为4096。
- show()方法应该返回写入缓冲区的字节数,也就是snprintf()的返回值。
- show()应始终使用snprintf()。
- store()应返回缓冲区的已用字节数,可使用strlen()。
- show()或store()可以返回错误值。当得到一个非法值,必须返回一个错误值。
-一个传递给方法的对象将会通过sysfs调用对象内嵌的引用计数固定在内存中。尽管如此,对象代表的物
理实体(如设备)可能已不存在。如有必要,应该实现一个检测机制。一个简单的(未经实验证实的)设备属性例程如下:
static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", dev->name);
}
static ssize_t store_name(struct device * dev, const char * buf)
{
sscanf(buf, "%20s", dev->name);
return strnlen(buf, PAGE_SIZE);
}
static DEVICE_ATTR(name, S_IRUGO, show_name, store_name);
(注意:真实的程序不允许用户空间设置设备名。)
~~~~~~~~~~~~~~~~~~~~~~~~~~
顶层目录
sysfs目录的安排显示了内核数据结构之间的关系。
:顶层sysfs目录如下:
block/
bus/
class/
devices/
firmware/
net/
fs/
devices/包含了一个设备树的文件系统表示。他直接以内核设备树的形式反映了设备的层次结构。
bus/包含了各种内核总线类型的固定目录布局。每个总线目录包含两个子目录:
devices/
drivers/
devices/包含了每个系统中出现的设备 指向 设备目录/dev的动态链接。
drivers/包含了一个每个已为特定总线上的设备而挂载的驱动程序的目录(这里假定驱动没有多个总线类型)。
fs/包含了一个为文件系统设立的目录。 现在每个想要导出属性的文件系统必须在fs/下创建自己的层次结构
below fs/(可参见./fuse.txt作为参考)。
更多有关driver-model的特性信息可以在Documentation/driver-model/.
Documentation/driver-model/中找到。
~~~~~~~~~~~~~~~~~~
当前接口
以下的接口层普遍出现在sysfs中:
- devices (include/linux/device.h)
-设备
----------------------------------
结构体:
struct device_attribute {
struct attributeattr;
ssize_t (*show)(struct device * dev, char * buf);
ssize_t (*store)(struct device * dev, const char * buf);
};
声明:
DEVICE_ATTR(_name, _str, _mode, _show, _store);
增/删属性:
int device_create_file(struct device *device, struct device_attribute * attr);
void device_remove_file(struct device * dev, struct device_attribute * attr);
- bus drivers (include/linux/device.h)
-总线驱动程序
--------------------------------------
结构体:
struct bus_attribute {
struct attributeattr;
ssize_t (*show)(struct bus_type *, char * buf);
ssize_t (*store)(struct bus_type *, const char * buf);
};
Declaring:声明:
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)
-设备驱动程序
-----------------------------------------
Structure:
结构体:
struct driver_attribute {
struct attributeattr;
ssize_t (*show)(struct device_driver *, char * buf);
ssize_t (*store)(struct device_driver *, const char * buf);
};
Declaring:
声明:
DRIVER_ATTR(_name, _mode, _show, _store)
增/删属性
int driver_create_file(struct device_driver *, struct driver_attribute *);
void driver_remove_file(struct device_driver *, struct driver_attribute *);