Kernel-3.10.0-957.el7_sysfs

Chinese translated version of Documentation/filesystems/sysfs.txt

If you have any comment or update to the content, please contact the
original document maintainer directly. However, if you have a problem
communicating in English you can also ask the Chinese maintainer for
help. Contact the Chinese maintainer if this translation is outdated
or if there is a problem with the translation.

Maintainer: Patrick Mochel mochel@osdl.org
Mike Murphy mamurph@cs.clemson.edu
Chinese maintainer: Fu Wei tekkamanninja@gmail.com


Documentation/filesystems/sysfs.txt 的中文翻译

如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
译存在问题,请联系中文版维护者。
英文版维护者: Patrick Mochel mochel@osdl.org
Mike Murphy mamurph@cs.clemson.edu
中文版维护者: 傅炜 Fu Wei tekkamanninja@gmail.com
中文版翻译者: 傅炜 Fu Wei tekkamanninja@gmail.com
中文版校译者: 傅炜 Fu Wei tekkamanninja@gmail.com

以下为正文

sysfs - 用于导出内核对象(kobject)的文件系统

Patrick Mochel mochel@osdl.org
Mike Murphy mamurph@cs.clemson.edu

修订: 16 August 2011
原始版本: 10 January 2003

sysfs 简介:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

sysfs 是一个最初基于 ramfs 且位于内存的文件系统。它提供导出内核
数据结构及其属性,以及它们之间的关联到用户空间的方法。

sysfs 始终与 kobject 的底层结构紧密相关。请阅读
Documentation/kobject.txt 文档以获得更多关于 kobject 接口的
信息。


使用 sysfs
~~~~~~~~~~~

只要内核配置中定义了 CONFIG_SYSFS ,sysfs 总是被编译进内核。你可
通过以下命令挂载它:

mount -t sysfs sysfs /sys


创建目录

任何 kobject 在系统中注册,就会有一个目录在 sysfs 中被创建。这个
目录是作为该 kobject 的父对象所在目录的子目录创建的,以准确地传递
内核的对象层次到用户空间。sysfs 中的顶层目录代表着内核对象层次的
共同祖先;例如:某些对象属于某个子系统。

Sysfs 在与其目录关联的 sysfs_dirent 对象中内部保存一个指向实现
目录的 kobject 的指针。以前,这个 kobject 指针被 sysfs 直接用于
kobject 文件打开和关闭的引用计数。而现在的 sysfs 实现中,kobject
引用计数只能通过 sysfs_schedule_callback() 函数直接修改。

属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296

kobject 的属性可在文件系统中以普通文件的形式导出。Sysfs 为属性定义
了面向文件 I/O 操作的方法,以提供对内核属性的读写。


属性应为 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);


一个单独的属性结构并不包含读写其属性值的方法。子系统最好为增删特定
对象类型的属性定义自己的属性结构体和封装函数。

例如:驱动程序模型定义的 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);
};

int device_create_file(struct device *, const struct device_attribute *);
void device_remove_file(struct device *, const struct device_attribute *);

为了定义设备属性,同时定义了一下辅助宏:

#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 操作,
以帮助读写调用实现属性所有者的显示和储存方法。

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

也就是说,他们应只以一个处理对象、一个属性和一个缓冲指针作为参数。

sysfs 会分配一个大小为 (PAGE_SIZE) 的缓冲区并传递给这个方法。
Sysfs 将会为每次读写操作调用一次这个方法。这使得这些方法在执行时
会出现以下的行为:

- 在读方面(read(2)),show() 方法应该填充整个缓冲区。回想属性
应只导出了一个属性值或是一个同类型属性值的数组,所以这个代价将
不会不太高。

这使得用户空间可以局部地读和任意的向前搜索整个文件。如果用户空间
向后搜索到零或使用‘0’偏移执行一个pread(2)操作,show()方法将
再次被调用,以重新填充缓存。

- 在写方面(write(2)),sysfs 希望在第一次写操作时得到整个缓冲区。
之后 Sysfs 传递整个缓冲区给 store() 方法。

当要写 sysfs 文件时,用户空间进程应首先读取整个文件,修该想要
改变的值,然后回写整个缓冲区。

在读写属性值时,属性方法的执行应操作相同的缓冲区。

注记:

- 写操作导致的 show() 方法重载,会忽略当前文件位置。

- 缓冲区应总是 PAGE_SIZE 大小。对于i386,这个值为4096。

- show() 方法应该返回写入缓冲区的字节数,也就是 snprintf()的
返回值。

- show() 应始终使用 snprintf()。

- store() 应返回缓冲区的已用字节数。如果整个缓存都已填满,只需返回
count 参数。

- show() 或 store() 可以返回错误值。当得到一个非法值,必须返回一个
错误值。

- 一个传递给方法的对象将会通过 sysfs 调用对象内嵌的引用计数固定在
内存中。尽管如此,对象代表的物理实体(如设备)可能已不存在。如有必要,
应该实现一个检测机制。

一个简单的(未经实验证实的)设备属性实现如下:

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/

devices/ 包含了一个设备树的文件系统表示。他直接映射了内部的内核
设备树,反映了设备的层次结构。

bus/ 包含了内核中各种总线类型的平面目录布局。每个总线目录包含两个
子目录:

devices/
drivers/

devices/ 包含了系统中出现的每个设备的符号链接,他们指向 root/ 下的
设备目录。

drivers/ 包含了每个已为特定总线上的设备而挂载的驱动程序的目录(这里
假定驱动没有跨越多个总线类型)。

fs/ 包含了一个为文件系统设立的目录。现在每个想要导出属性的文件系统必须
在 fs/ 下创建自己的层次结构(参见Documentation/filesystems/fuse.txt)。

dev/ 包含两个子目录: char/ 和 block/。在这两个子目录中,有以
<major>:<minor> 格式命名的符号链接。这些符号链接指向 sysfs 目录
中相应的设备。/sys/dev 提供一个通过一个 stat(2) 操作结果,查找
设备 sysfs 接口快捷的方法。

更多有关 driver-model 的特性信息可以在 Documentation/driver-model/
中找到。


TODO: 完成这一节。


当前接口
~~~~~~~~

以下的接口层普遍存在于当前的sysfs中:

- 设备 (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);


- 总线驱动程序 (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 *);


- 设备驱动程序 (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,其自身的稳定和适当的文档是非常重要的。所有新的 sysfs
属性必须在 Documentation/ABI 中有文档。详见 Documentation/ABI/README。