linuxsysfs.txt文件学习
sysfs一种导出内核对象的文件系统,总是被挂载在 /sys 挂载点上。
1. sysfs是什么
sysfs 是一种基于 ramfs实现的内存文件系统。它提供一种导出内核数据结构(data structures)、属性(attributes)和他们之间的连接给用户空间。
Sysfs和kobject结构(infrastructure)有内在的联系,请阅读Documentation/kobject.txt获取更多关于kobject接口的信息。
2. sysfs的使用
如果定义了CONFIG_SYSFS就会编译sysfs到内核,可通过下面的命令来访问
Mount –t sysfs sysfs /sys
3. 创建目录(DirectoryCreation)
系统注册的每个kobject,sysfs都会创建对应的目录。此目录作为kobject父目录的子目录来创建,向用户空间描述了内部object的分层结构。Sysfs顶层目录代表object共同的父目录。
Sysfs内存存储了一个指向kobject的指针,kobject结构体定义:
struct kobject {
const char *name;
struct list_head entry;
struct kobject *parent;
struct kset *kset;
struct kobj_type *ktype;
struct sysfs_dirent *sd;
struct kref kref;
unsigned int state_initialized:1;
unsignedint state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
};
用 struct sysfs_dirent 的结构用于表示 /sys 中的每一个目录项。
之前,在文件打开或是关闭的时候,sysfs使用这个kobject指针来作为kobject的引用统计。现在的sysfs实现中,只通过sysfs_schedule_callback()来修改kobject引用统计。
4. 属性(Attributes)
在文件系统中,kobject的属性作为普通文件导出。Sysfs提供文件操作的方法来访问属性,从而提供了一种读写内核属性的方法(Sysfsforwards file I/O operations to methods defined for the attributes, providing ameans to read and write kernel attributes)。
属性应该是ASCII文本文件,每个文件只有一个值最好,但是如果每个文件只包含一个值效率不高。所以最合适的方式是一个文件包含一组同类型值。
混合类型表示多行数据和奇怪的数据格式是非常不合适的,如果这么做,将被别人怀疑和在没有通知情况下重写你的代码。
一个属性定义非常简单:
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);
一个空属性没有包含读或是写此属性值的方法。子系统(Subsystem)被鼓励定义自己的属性结构和打包一个具体对象类型的属性的增加与删除函数。
比如,驱动模型如下定义了device_attribute结构体:
struct device_attribute {
structattribute 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 *);
同时定义helper来定义设备属性
#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,
},
};
5. 子系统特定的回调(Subsystem-SpecificCallbacks)
当一个子系统定了一个新的属性类型,它必须实现一组读写sysfs操作,读写操作对应属性拥有者的show和store方法。
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *, char *);
ssize_t (*store)(struct kobject *, struct attribute *, const char *,size_t);
};
子系统应该已经定义了用来描述属性类型的kobj_type,kobj_type保存了sysfs_ops指针。
当读或写文件的时候,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",
(unsignedlong)dev_attr->show);
}
return ret;
}
6. 读/写属性数据(Reading/Writing Attribute Data)
要读/写属性,在声明属性的时候必须明确指定show()或是store()函数。函数类型像下面设备属性的方法这样定义:
ssize_t (*show)(struct device *dev, structdevice_attribute *attr, char *buf);
ssize_t (*store)(struct device *dev, structdevice_attribute *attr,
const char *buf, size_tcount);
换句话说,这些方法值需要带一个object、一个attribute和一个buffer作为参数。
Sysfs分配一个buffer大小(PAGE_size)和传递它给函数,用户层的每次读或是写,sysfs就会相应的调用这些函数。这就要求这些方法的实现需要遵循下面的行为:
(1) Read操作,show()组要填写整个buffer,回忆前面提到的,一个属性只导出一个值或是同类型的数组值,所以开销不大。
这样运行用户空间读取部分内容和整个文件任何部分,如果用户空间搜索(seek back)到0或是偏移地址为0,show()会被重新调用,重新初始化且填充buffer。
(2) Write操作,sysfs期望在第1次写时整个buffer都传递进来,sysfs然后传递整个buffer给store()。
当写sysfs文件时,用户空间进程应先读取整个文件,然后修改希望改变的值,再重新写回整个buffer。
属性函数的实现,在读写值的时候应该操作同一个buffer。
(3) 其他需要注意的
1) 写操作触发show()的重置,不管当前的文件位置。
2) Buffer大小总是PAGE_SIZE大小,在i386上是4096个字节。
3) Show()应返回要读取bufffer的字节数,这通过返回scnprintf()来实现。
4) Show()一般都会使用scnprintf()。
5) Store()应返回要写入buffer的字节数,如果整个buffer已经施工,就只返回count参数。
6) Show()或是store()可常常返回错误,如果发生错误,一定要返回一个error值。
7) 在内存中,通过sysfs引用计数它的嵌入object,而使其和object紧密相连。然而,物理实体(比如device)这个object可能不存在,所以需要时确保有种方式来检查。
一个简单的设备属性的实现如下:
static ssize_t show_name(struct device*dev, struct device_attribute *attr,
char *buf)
{
returnscnprintf(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);
returncount;
}
static DEVICE_ATTR(name, S_IRUGO,show_name, store_name);
请注意,实际实现中不允许用户空间设置设备名字。
7. 顶层目录布局(Top LevelDirectory Layout)
Sysfs目录布置暴露了内核数据结构的关系,顶层sysfs目录如下:
block/
bus/
class/
dev/
devices/
firmware/
net/
fs/
(1) Devices
包含代表了设备树的文件系统,它直接映射设备层次的内部内核设备树。
图1
(2) Bus
包含内核中不同总线类型的扁平化目录布局,每个总线的目录包含devices/和drivers这两个子目录。
图2
1) Devices
包含系统发现每个设备的符号连接,这些符号连接指向根目录/下的设备目录。
图3
2) Drivers
包含特定总线上每个特定总线上设备的设备驱动目录(假设驱动没有跨越多个总线类型)。
图4
(3) Fs
包含一些文件系统目录,目前,每个文件系统如想要导出属性,必须在fs/下创建自己的层次结构。
图5
(4) Dev
包含char/和block/两个目录,这两个目录下都是以<major>:<minor>命名的符号链接,这些符号链接指向给定设备(given device)的sysfs目录。/sys/dev提供一个快速查找通过stat操作得到设备的sysfs接口。
图6
8. 当前接口(CurrentInterfaces)
当前sysfs有下面的几种接口,分别是设备、总线、设备驱动
(1) devices(include/linux/device.h)
结构体:
struct device_attribute {
structattribute 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);
(2) bus drivers(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 *);
(3) 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(structdevice_driver *, const struct driver_attribute *);
(4) class (include/linux/device.h)
结构体:
struct class_attribute {
structattribute attr;
ssize_t(*show)(struct class *class, struct class_attribute *attr,
char*buf);
ssize_t(*store)(struct class *class, struct class_attribute *attr,
constchar *buf, size_t count);
constvoid *(*namespace)(struct class *class,
const struct class_attribute *attr);
};
声明:
CLASS_ATTR(_name, _mode, _show, _store)
int __must_check class_create_file(structclass *class,
const struct class_attribute *attr);
void class_remove_file(struct class *class,const struct class_attribute *attr);
9. 文档(Documentation)
Sysfs目录结构和每个目录下的属性定义一个内核与用户空间的ABI,对于任何ABI(应用程序二进制接口,ABI-ApplicationBinary Interface),稳定性和合适的文档化很重要。所有新的sysfs属性必须在记录在Documentation/ABI中,更多的信息可通过Documentation/ABI/README获取。
参考\kernel\Documentation\filesystems\sysfs.txt
使用 /sys 文件系统访问 Linux 内核
http://www.ibm.com/developerworks/cn/linux/l-cn-sysfs/
Documentation/filesystems/sysfs.txt译文
http://blog.csdn.net/tuzhutuzhu/article/details/21344639