sysfs 的實作與應用詳解
透過 Linux v2.6.34 的原始碼解釋 sysfs 的實作與應用。
歷史
sysfs 的開發,目的是為了解決 procfs 不夠階層化的問題,並將有關設備的資訊從 procfs 中抽離。最早 sysfs 在實作時,以 ramfs 為基礎,被稱作 ddfs (Device Driver Filesystem),後來在 Linux 2.5 更名為 driverfs。
在 Linux 2.6 時設計了一個 kobject 子系統,拋棄了 ramfs,利用 kobject 來建立這些裝置的資訊。kobject 所提供的階層化組織,將會直接反映在 sysfs 上,因此可以很輕鬆的建出一系列有組織的目錄。後來發現 kobject 不只可以用在裝置模型上,也被證明在其他的核心子系統也同樣很有用,因此 driverfs 最後被更名為 sysfs,並通常掛載在 /sys
下。
因為本身就源自於 procfs 的設計思路,因此它所提供的也是 user space 與 kernel space 交換資訊的介面,同樣可以輕鬆透過 cat 或 echo 來與核心溝通。也由於系統的所有設備與匯流排都是透過 kobject 組織的,所以 sysfs 提供了系統的硬體拓樸面向 user space 的一種介面。
裝置模型
實作裝置模型最初的動機是:電源管理,因此實作一個樹狀的裝置拓樸是勢在必行的,這棵樹可以明確的看出哪個驅動程式連接到哪個控制器,哪個裝置連接到哪個匯流排,如此核心就可以透過裝置樹來以正確的順序關閉電源。
資料結構
kobject
裝置模型實作的中心為 kobject,kobject 提供了這些物件操作:
- reference count
- 管理物件的鏈結串列(集合)
- 集合的 spinlock
- 將物件屬性導出到使用者空間(透過sysfs)
|
|
kobject 的使用是直接將其嵌入到其他資料結構中,用作核心物件的基礎。
kobj_type
kobj_type 用於描述一系列 kobject 的預設行為。因此,不需要每個 kobject 都定義自己的行為,可以把行為儲存在 kobj_type 中,相同類型的 kobject 會指向相同的 kobj_type。
|
|
kset
很多時候必須將不同物件的集合歸類到集合中,例如,所有 character device 的集合,或是所有基於 PCI 的設備集合。kset 透過鏈結串列將相同物件集合起來。
|
|
kref
kref 用來儲存 kobject 的 reference count。
|
|
關係圖
sysfs
sysfs 跟 procfs 相同,是一個位於記憶體的虛擬檔案系統,它為我們提供了 kobject 階層結構的視圖,讓用戶可以將裝置拓樸視為一個簡單的檔案系統。sysfs 將 kobject 的屬性匯出成檔案,將核心變數提供給 user space 做讀取與寫入。
資料結構
sysfs_dirent
|
|
s_active
是比較特別的屬性,用來解決 user 打開 sysfs 節點,就可以防止核心刪除 kobject 對應的實體的問題。因此當每次操作內部物件時,需要先呼叫 sysfs_get_active
取得活動引用,在結束操作時,必須立即使用 sysfs_put_active
釋放活動引用。
在刪除一個 sysfs 檔時,呼叫 sysfs_deactivate
將 active_count 設為負值,在這個計數器為負值時,就不能對 kobject 進行操作了。在 kobject 的所有使用者都消失時,核心才可以安全的刪除它,但是 sysfs 下的檔案和 sysfs_dirent 實體仍然會繼續存在。
s_active: [PATCH] sysfs: implement sysfs_dirent active reference and immediate disconnect
sysfs_dirent 下有一個匿名的union結構,用來描述檔案類型:
sysfs_elem_dir
為目錄,指向一個 kobjectsysfs_elem_symlink
為符號連結,指向另一個 sysfs_direntsysfs_elem_attr
為普通屬性,指向 attributesysfs_elem_bin_attr
為二進位屬性,指向 bin_attribute
|
|
attribute
普通屬性下有一個 attribute 的成員,用來描述被匯出到 sysfs 時的檔案名稱、存取權限與所屬使用者。
|
|
bin_attribute
除了 attribute 的屬性,因為對於二進位檔案用於讀取與寫入的方法不盡相同,所以在資料結構中定義了這些 function pointer。
|
|
sysfs_ops
show
函式會在執行 read 時被呼叫,store
函式會在執行 write 時被呼叫。
|
|
sysfs_dirent 與 dentry 的關係
dentry 中的 d_fsdata
,指向一個 sysfs_dirent,並透過 s_dir.children
串起整個目錄下的文件。
mount
sysfs 的 mount 透過 sysfs_fill_super
函式,建立 root inode 與 dentry,將 dentry 的 d_fsdata
指向靜態宣告的 sysfs_root,填上 superblock 的 root。
file_operations
sysfs_buffer
為了方便 user space 與 sysfs 交換資料,核心設計了 sysfs_buffer 這個資料結構。
open
在 open 時,首先透過 file 取得 kobject,指定 ops 為 kobject 內的 ktype 上的 sysfs_ops
,檢查寫與 store、檢查讀與 show,沒問題的話就分配一個 sysfs_buffer,最後將 file 指標中的 private_data
關連到 sysfs_buffer。
read
若是在第一次讀取或是寫入後,就呼叫 show 函式來 refresh sysfs_buffer。最後將 sysfs_buffer 內的資料複製到參數傳入的 buf。
write
分配一個新的 page,再呼叫 store 函式。
範例
devfs & udev
devfs 出現在 Linux 2.4,devfs 使用動態註冊裝置,提供一個新的,更合理的方式管理那些在 /dev
下的所有 block device 與 character device。
缺陷
- 裝置名稱不夠彈性
- major/minor number 無法動態綁定
- 裝置資訊放在 kernel memory,kernel memory 無法 swap out
在 sysfs 獲得成功後,udev 也在 Linux 2.6 出現,成功取代 devfs。udev 解決了 devfs 的缺陷,它執行在 user space,並透過熱插拔 (/sbin/hotplug) 時產生的uevent,與 sysfs 提供的裝置資訊,動態調整 /dev
下的設備。
Toy Example
這個範例是一個 kernel module,一開始會建立一個名稱為 dev1 的 device_attribute,傳入 dev1_store
、dev1_show
的 function pointer,透過核心提供的 API 操作 kobject,最後把 dev1 放在 /sys/device/sysfs_demo/subdir1/subdir2/
下。如果對 dev1 寫入 0,會將 subdir2 下的符號連結與 subdir3 移除。
參考資料
Linux Kernel v2.6.34 Source Code
Linux Kernel Development 3/e, Robert Love
Professional Linux Kernel Architecture, Wolfgang Mauerer
The sysfs Filesystem, Patrick Moch
udev – A Userspace Implementation of devfs, Greg Kroah-Hartman