sysfs - 用於匯出核心物件的_The_檔案系統¶
Patrick Mochel <mochel@osdl.org>
Mike Murphy <mamurph@cs.clemson.edu>
- 修訂:
2011 年 8 月 16 日
- 原始:
2003 年 1 月 10 日
它是什麼¶
sysfs 是一個基於 RAM 的檔案系統,最初基於 ramfs。它提供了一種將核心資料結構、它們的屬性以及它們之間的連結匯出到使用者空間的方法。
sysfs 本質上與 kobject 基礎設施相關聯。請閱讀 您一直不想知道的關於 kobject、kset 和 ktype 的一切 以獲取有關 kobject 介面的更多資訊。
使用 sysfs¶
如果定義了 CONFIG_SYSFS,則始終編譯 sysfs。您可以透過執行以下操作來訪問它
mount -t sysfs sysfs /sys
目錄建立¶
對於每個向系統註冊的 kobject,都會在 sysfs 中為其建立一個目錄。該目錄是作為 kobject 父目錄的子目錄建立的,從而向用戶空間表達了內部物件層次結構。sysfs 中的頂級目錄表示物件層次結構的公共祖先;即物件所屬的子系統。
sysfs 在內部儲存一個指向 kobject 的指標,該 kobject 在與目錄關聯的 kernfs_node 物件中實現了目錄。過去,每當檔案開啟或關閉時,sysfs 都使用此 kobject 指標直接對 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);
裸屬性不包含讀取或寫入屬性值的方法。鼓勵子系統定義自己的屬性結構和包裝函式,以便為特定物件型別新增和刪除屬性。
例如,驅動程式模型定義 struct 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,
};
請注意,正如 include/linux/kernel.h 中所述的“OTHER_WRITABLE?通常被認為是一個壞主意。”,因此嘗試將 sysfs 檔案設定為每個人都可寫將失敗,從而恢復為“其他”的 RO 模式。
對於常見情況,sysfs.h 提供了方便的宏,使定義屬性更容易,並使程式碼更簡潔易讀。以上情況可以縮短為
static struct device_attribute dev_attr_foo = __ATTR_RW(foo);
可用於定義包裝函式的助手列表為
- __ATTR_RO(name)
假定預設的 name_show 和 mode 0444
- __ATTR_WO(name)
僅假定 name_store 並且僅限於 mode 0200,即僅 root 寫入訪問。
- __ATTR_RO_MODE(name, mode)
用於更嚴格的 RO 訪問;目前唯一的用例是 EFI 系統資源表(參見 drivers/firmware/efi/esrt.c)
- __ATTR_RW(name)
假定預設的 name_show、name_store 並將模式設定為 0644。
- __ATTR_NULL
這將名稱設定為 NULL,並用作列表結束指示符(參見:kernel/workqueue.c)
子系統特定回撥¶
當子系統定義新的屬性型別時,它必須實現一組 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);
};
[ 子系統應該已經定義了一個 struct kobj_type 作為此型別的描述符,sysfs_ops 指標儲存在該型別中。有關更多資訊,請參見 kobject 文件。]
當讀取或寫入檔案時,sysfs 會呼叫該型別的適當方法。然後,該方法將通用的 struct kobject 和 struct 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_streq()這樣的函式可以安全使用。在寫入 sysfs 檔案時,使用者空間程序應首先讀取整個檔案,修改它希望更改的值,然後將整個緩衝區寫回。
屬性方法實現應在讀取和寫入值時操作相同的緩衝區。
其他說明
無論當前檔案位置如何,寫入都會重新準備 show() 方法。
緩衝區的長度始終為 PAGE_SIZE 位元組。在 x86 上,這是 4096。
show() 方法應返回列印到緩衝區中的位元組數。
show() 應該只使用
sysfs_emit()或sysfs_emit_at()來格式化要返回給使用者空間的值。store() 應返回緩衝區中使用的位元組數。如果已使用整個緩衝區,則只需返回計數引數。
show() 或 store() 始終可以返回錯誤。如果出現錯誤值,請務必返回錯誤。
透過 sysfs 引用計數其嵌入物件,傳遞給方法的物件將被固定在記憶體中。但是,該物件表示的物理實體(例如裝置)可能不存在。如有必要,請務必有一種方法來檢查這一點。
裝置屬性的一個非常簡單(且幼稚)的實現是
static ssize_t show_name(struct device *dev, struct device_attribute *attr,
char *buf)
{
return sysfs_emit(buf, "%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/
fs/
hypervisor/
kernel/
module/
net/
power/
devices/ 包含裝置樹的檔案系統表示形式。它直接對映到內部核心裝置樹,該樹是 struct device 的層次結構。
bus/ 包含核心中各種匯流排型別的平面目錄佈局。每個匯流排的目錄包含兩個子目錄
devices/
drivers/
devices/ 包含系統中發現的每個裝置的符號連結,這些連結指向根目錄下的裝置目錄。
drivers/ 包含為該特定總線上的裝置載入的每個裝置驅動程式的目錄(這假定驅動程式不跨越多種匯流排型別)。
fs/ 包含一些檔案系統的目錄。目前,每個想要匯出屬性的檔案系統都必須在 fs/ 下建立自己的層次結構(有關示例,請參見 ./fuse.rst)。
module/ 包含所有已載入的系統模組(包括內建模組和可載入模組)的引數值和狀態資訊。
dev/ 包含兩個目錄:char/ 和 block/。在這兩個目錄中,都有名為 <major>:<minor> 的符號連結。這些符號連結指向給定裝置的 sysfs 目錄。/sys/dev 提供了一種從 stat(2) 操作的結果中快速查詢裝置 sysfs 介面的方法。
有關驅動程式模型特定功能的更多資訊,請參見 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);
};
宣告
static BUS_ATTR_RW(name);
static BUS_ATTR_RO(name);
static BUS_ATTR_WO(name);
建立/刪除
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_RO(_name)
DRIVER_ATTR_RW(_name)
建立/刪除
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 以獲取更多資訊。