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
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
|
SPDX-License-Identifier: GPL-2.0
Chinese translated version of Documentation/filesystems/sysfs.rst
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.rst 的中文翻譯
如果想評論或更新本文的內容,請直接聯繫原文檔的維護者。如果你使用英文
交流有困難的話,也可以向中文版維護者求助。如果本翻譯更新不及時或者翻
譯存在問題,請聯繫中文版維護者。
英文版維護者: 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>
繁體中文版校譯者:胡皓文 Hu Haowen <2023002089@link.tyut.edu.cn>
以下爲正文
---------------------------------------------------------------------
sysfs - 用於導出內核對象(kobject)的文件系統
Patrick Mochel <mochel@osdl.org>
Mike Murphy <mamurph@cs.clemson.edu>
修訂: 16 August 2011
原始版本: 10 January 2003
sysfs 簡介:
~~~~~~~~~~
sysfs 是一個最初基於 ramfs 且位於內存的文件系統。它提供導出內核
數據結構及其屬性,以及它們之間的關聯到用戶空間的方法。
sysfs 始終與 kobject 的底層結構緊密相關。請閱讀
Documentation/core-api/kobject.rst 文檔以獲得更多關於 kobject 接口的
信息。
使用 sysfs
~~~~~~~~~~~
只要內核配置中定義了 CONFIG_SYSFS ,sysfs 總是被編譯進內核。你可
通過以下命令掛載它:
mount -t sysfs sysfs /sys
創建目錄
~~~~~~~~
任何 kobject 在系統中註冊,就會有一個目錄在 sysfs 中被創建。這個
目錄是作爲該 kobject 的父對象所在目錄的子目錄創建的,以準確地傳遞
內核的對象層次到用戶空間。sysfs 中的頂層目錄代表着內核對象層次的
共同祖先;例如:某些對象屬於某個子系統。
Sysfs 在與其目錄關聯的 kernfs_node 對象中內部保存一個指向實現
目錄的 kobject 的指針。以前,這個 kobject 指針被 sysfs 直接用於
kobject 文件打開和關閉的引用計數。而現在的 sysfs 實現中,kobject
引用計數只能通過 sysfs_schedule_callback() 函數直接修改。
屬性
~~~~
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_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 = kobj_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) {
printk("dev_attr_show: %pS returned bad count\n",
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() 方法應該返回寫入緩衝區的字節數,也就是 scnprintf()的
返回值。
- show() 方法在將格式化返回值返回用戶空間的時候,禁止使用snprintf()。
如果可以保證不會發生緩衝區溢出,可以使用sprintf(),否則必須使用
scnprintf()。
- 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.rst)。
dev/ 包含兩個子目錄: char/ 和 block/。在這兩個子目錄中,有以
<major>:<minor> 格式命名的符號鏈接。這些符號鏈接指向 sysfs 目錄
中相應的設備。/sys/dev 提供一個通過一個 stat(2) 操作結果,查找
設備 sysfs 接口快捷的方法。
更多有關 driver-model 的特性信息可以在 Documentation/driver-api/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)(const struct bus_type *, char * buf);
ssize_t (*store)(const 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。
|