网络上关于sysfs的这篇译文有很多,为什么还要多此一举呢?
一来是觉得阅读原版的文档更加有助于理解,二来计划两个月后参加托业考试。英语已经放下好几年了,想要尽快的拾起来。
过去,当文件开/关时,sysfs使用这个kobject指针来做对kobject的引用计数。现在,使用函数sysfs_schedule_callback()来修改kobject的引用计数。
在文件系统中,将kobjects的属性导出为普通文件。sysfs将文件操作提升为属性规则,以此来读写内核属性。
属性必须是ASCII文本文件,最好每个文件只有一个值。每个文件只有一个值可能效率不高,所以现实中通常将同一类行的一组数据放在一个文件中。
不同类型,表示多行数据,奇怪的数据形式等做法都是不合适的。一旦这么做了,那么会被人鄙视,并且代码会被人重写。
定义一个属性是很简单的:
struct attribute { char * name; struct module *owner; mode_t mode; };
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr); void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr);仅仅一个属性不包含读写属性值的方法。子系统定义自己的属性结构,并且封装函数来对特定的object类型增加和删除属性。
举个例子,驱动类型这样定义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 *);同时,定义了下面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, }, };
当一个子系统定义一种属性类型,则它必须定义一组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,并用来存sysfs_ops的指针。]
当读/写文件时,sysfs调用该类型对应的函数(method)。该函数将通用的struct kobject和struct attribute指针翻译成相应的指针类型,并调用相应的函数。
说明如下:
#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);也就是说,函数应该只有三个参数:一个object,一个属性和一个buf。
sysfs分配一个page的buf,然后将它作为参数传递给函数。当有读/写操作时,sysfs会调用相应的show/store函数。这就造就了如下的读写规则:
- 读时,show()会写整个buf。回想之前所说的:一个属性只映射一个值或一个简单的数组值,所以这样的开销不大。
允许用户空间根据需要,读文件的任意部分内容。如果用户空间从头开始或者pread时offset为0,show()将再次被调用,重新填充buffer。
- 写时,sysfs期待第一次写时将整个buffer传进来。sysfs之后将整个buf传给store()函数。
写sysfs文件时,用户空间程序应该读整个文件,修改需要修改的值,然后写全部buffer。
属性函数操作时,读写时应该操作一个相同的buffer。
其他注意事项:
- 写操作会造成show()函数重建而忽略当前的文件位置。
- buffer大小总是PAGE_SIZE的byte大小。
- sysfs通过引用计算(referencing counting)其嵌入的object将show()和store()与内存相关联。
一个简单的设定设备属性的方法如下:
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目录如下所示:
block/ bus/ class/ dev/ devices/ firmware/ net/ fs/device/ 包含设备树的文件系统形式。它直接映射struct device层次的内核设备树。
bus/ 包含内核中的各种总线类型的平的目录布局(flat directory layout)。每个bus目录包含两个子目录:
devices/ drivers/devices/ 包含系统中发现的指向root/目录下各设备目录的符号链接。
fs/ 包含一个各个文件系统的目录。当前,每个文件系统想要export属性,必须在fs/下创建一个自己的等级。
dev/ 包含char/和block/两个目录。这两个目录下都是以<major>:<minor>方式命名的符号链接。这些符号链接指向特定的sysfs目录。/sys/dev提供一种快速查询通过stat操作得到的设备接口。
当前的sysfs中有如下的interface 层:
- devices (include/linux/device.h) ---------------------------------- Structure: 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); }; Declaring: DEVICE_ATTR(_name, _mode, _show, _store); Creation/Removal: 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 drivers (include/linux/device.h) -------------------------------------- Structure: 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); }; Declaring: BUS_ATTR(_name, _mode, _show, _store) Creation/Removal: 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 attribute attr; ssize_t (*show)(struct device_driver *, char * buf); ssize_t (*store)(struct device_driver *, const char * buf, size_t count); }; Declaring: DRIVER_ATTR(_name, _mode, _show, _store) Creation/Removal: 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来获取更多信息。
Documentation/filesystems/sysfs.txt译文,布布扣,bubuko.com
Documentation/filesystems/sysfs.txt译文
原文:http://blog.csdn.net/tuzhutuzhu/article/details/21344639