Linux裝置驅動模型框架分析(五)——LDDM的展現:sysfs
sysfs是一個基於RAM的檔案系統,它和kobject一起,可以將Kernel的資料結構匯出到使用者空間,以檔案目錄結構的形式,提供對這些資料結構(以及資料結構的屬性)的訪問支援。
sysfs目錄 |
所包含內容 |
/sys/devices |
這是核心對系統中所有裝置的分層次表達模型,也是 /sys 檔案系統管理裝置的最重要的目錄結構 |
/sys/dev |
這個目錄下維護一個按字元裝置和塊裝置的主次號碼(major:minor)連結到真實的裝置(/sys/devices下)的符號連結檔案 |
/sys/bus |
這是核心裝置按匯流排型別分層放置的目錄結構, devices 中的所有裝置都是連線於某種匯流排之下,在這裡的每一種具體匯流排之下可以找到每一個具體裝置的符號連結,它也是構成 |
/sys/class |
這是按照裝置功能分類的裝置模型,如系統所有輸入裝置都會出現在 /sys/class/input之下,而不論它們是以何種匯流排連線到系統。它也是構成Linux統一裝置模型的一部分; |
/sys/block(stale) |
這裡是系統中當前所有的塊裝置所在,按照功能來說放置在 /sys/class 之下會更合適,但只是由於歷史遺留因素而一直存在於/sys/block,但從2.6.22開始就已標記為過時,只有在打開了CONFIG_SYSFS_DEPRECATED配置下編譯才會有這個目錄的存在,並且在2.6.26核心中已正式移到/sys/class/block, |
/sys/firmware |
這裡是系統載入韌體機制的對使用者空間的介面,關於韌體有專用於韌體載入的一套API |
/sys/fs |
這裡按照設計是用於描述系統中所有檔案系統,包括檔案系統本身和按檔案系統分類存放的已掛載點; |
/sys/kernel |
這裡是核心所有可調整引數的位置; |
/sys/module |
這裡有系統中所有模組的資訊,不論這些模組是以內聯(inlined)方式編譯到核心映像檔案(vmlinuz)中還是編譯為外部模組(ko檔案),都可能會出現在/sys/module 編譯為外部模組(ko檔案)在載入後會出現對應的/sys/module/<module_name>/,並且在這個目錄下會出現一些屬性檔案和屬性目錄來表示此外部模組的一些資訊,如版本號、載入狀態、所提供的驅動程式等; 編譯為內聯方式的模組則只在當它有非0屬性的模組引數時會出現對應的/sys/module/<module_name>,這些模組的可用引數會出現在/sys/modules/<modname>/parameters/<param_name>中, 如 /sys/module/printk/parameters/time 這個可讀寫引數控制著內聯模組printk在列印核心訊息時是否加上時間字首; 所有內聯模組的引數也可以由 "<module_name>.<param_name>=<value>"的形式寫在核心啟動引數上,如啟動核心時加上引數"printk.time=1"與 向"/sys/module/printk/parameters/time"寫入1的效果相同; 沒有非0屬性引數的內聯模組不會出現於此。 |
/sys/power |
這裡是系統中電源選項,這個目錄下有幾個屬性檔案可以用於控制整個機器的電源狀態,如可以向其中寫入控制命令讓機器關機、重啟等。 |
前面已經講過sysfs和kobject等的關係了,這裡不再贅述,下面主要說一下屬性和sysfs與普通檔案系統的關係。
前面已經看到的跟屬性相關的關鍵資料結構有:
l attribute
l bin_attribute
l attribute_group
attribute、bin_attribute和attribute_group
前面講過,屬性對應sysfs中的一個檔案,那麼在sysfs中,為什麼會有attribute的概念呢?sysfs中的目錄描述了kobject,而kobject是特定資料型別變數(如struct device)的體現。因此kobject的屬性,就是這些變數的屬性。它可以是任何東西,名稱、一個內部變數、一個字串等等。而attribute,在sysfs檔案系統中是以檔案的形式提供的,即:kobject的所有屬性,都在它對應的sysfs目錄下以檔案的形式呈現。這些檔案一般是可讀、寫的,而kernel中定義了這些屬性的模組,會根據使用者空間的讀寫操作,記錄和返回這些attribute的值。
總之,所謂的attibute,就是核心空間和使用者空間進行資訊互動的一種方法。例如某個driver定義了一個變數,卻希望使用者空間程式可以修改該變數,以控制driver的執行行為,那麼就可以將該變數以sysfs attribute的形式開放出來。
屬性分為普通屬性和二進位制屬性
1.普通屬性
name,屬性的名字,對應sysfs中檔案的名字。
mode,應用於屬性(檔案)的保護位,與檔案的許可權相同。
2.二進位制屬性
attr,包含的普通屬性。
size,二進位制屬性的最大長度,如果沒有最大值可以設為0。
private,私有資料,與linux中大部分私有資料的用途一樣,用於傳遞私有資料結構。
read()、write()、mmap(),操作二進位制屬性的函式。
二進位制屬性不能作為預設屬性被設定,只能顯式地建立。
使用該attribute生成的sysfs檔案,只能用字串的形式讀寫。而struct bin_attribute在struct attribute的基礎上,增加了read、write等函式,因此它所生成的sysfs檔案可以用任何方式讀寫。韌體一般使用bin_attribute屬性。
3.屬性組
name,屬性組的名字,不為空的話對應一個sysfs的資料夾。
attrs,屬性組裡的普通屬性列表。
bin_attrs,屬性組裡的二進位制屬性列表。
is_visible(),返回組中屬性的讀寫許可權。
sysfs_ops
屬性通過sysfs_ops進行讀寫。
show,顯示屬性值。
store,儲存屬性值。
sysfs與普通檔案系統的關係
sysfs中的目錄和檔案與kobject和attribute對應,而普通檔案系統是file和inode。sysfs在上層也是通過普通的read(),write()等系統呼叫進行操作的,那麼需要將file_operations轉換成最終的show()/store()。怎麼轉?因為屬性都是屬於kobject的,所以最後的讀寫肯定與kobject有關,先從kobject找找線索,看前面kobject有個屬性——sd,kernfs_node資料結構,是kobject在sysfs的表示。我們從分析這個核心資料結構入手。
kernfs_node
count、active,相關的計數,原子的。
parent,本節點的父節點,這個比較重要,屬性是檔案,父節點才是kobject。
name,節點名字。
rb,紅黑樹節點。
ns、has,名稱空間相關。
dir、symlink、attr,節點的型別,表示該節點是目錄、符號連結還是屬性。三個屬性定義在聯合體中,我們在這關注是的attr。
priv,私有資料,看到私有資料應該推測,有用的東西就在這裡傳遞,事實也是如此,在本節描述的訪問屬性的場景下,kobject就作為私有資料隨kernfs_node傳遞。
flags、mode,與檔案的相關屬性含義一致。
ino,推測是子裝置號。
iattr,節點本身的屬性。
那麼file如何轉換成kernfs_node的呢,類比一下,普通檔案有file和inode,這裡kernfs_node應該是相當於inode,那還應該有代表檔案的結構與之對應,沒錯,是有這個結構。這個結構是什麼,不妨先在kernfs_node中找線索。既然我們的研究物件是屬性,那就看看聯合裡的attr對應的資料結構。
kernfs_elem_attr
ops,對於kernfs的操作函式。
open,開啟的kernfs_node。
size,沒有查到用途,不影響分析。
notify_next,通知kernfs檔案,具體功能不詳,不影響分析。
只關注ops,目前linux中基本所有的操作都獨立抽象出了資料結構,這個資料結構應該是操作kernfs的函式,可能與我們找的答案有關。
kernfs_ops
atomic_write_len,寫是以kernel page為邊界的,如果沒有設定atomic_write_len,超過PAGE_SIZE的寫操作會被分成幾個操作進行。如果設定了atomic_write_len,那麼超過這個設定值的寫操作,atomic_write_len以內的部分是原子寫,超過的部分直接返回-E2BIG。
prealloc,設定了用mmap,不設定用read/write。因為read/wirte有自己的buf,與prealloc的buf不相容。
seq_show、seq_start、seq_next、seq_stop,順序檔案操作。下面會詳細分析。這裡需要關注他們的引數。
read、write、mmap,讀,寫,記憶體對映函式,這裡關注他們的引數。
看一下seq_xx和read的原型,引數分別為seq_file和kernfs_open_file。從名字上看kernfs_open_file和kernfs_node的關係,很像file和inode的關係。不妨先看看kernfs_open_file。
kernfs_open_file
kn,所屬的目錄節點。
file,代表開啟檔案。
priv,私有資料結構。
mutex,互斥體。
list,確實沒有查到在哪裡呼叫,暫時推測不出用途,不影響分析。
pralloc_buf,mmap使用。
atomic_write_len,同kernfs_elem_attr。
mmaped,是否已經進行了ioremap。
vm_ops,虛擬記憶體管理操作。
從核心類的成員可能得出這樣的推測,kernfs_open_file.file儲存普通檔案的file指標,在進行讀寫是用container_of()從file裡獲得kernfs_open_file結構體。以前的核心可能是這麼做的,但是現在不是這樣的。這裡要提到上面數的另一個核心資料結構seq_file。
seq_file
seq_file是為proc檔案系統設計的,來歷是這樣的,由於procfs的預設操作函式只使用一頁的快取,在處理較大的proc檔案時就有點麻煩,並且在輸出一系列結構體中的資料時也比較不靈活,需要自己在read_proc函式中實現迭代,容易出現Bug。所以核心黑客們對一些/proc程式碼做了研究,抽象出共性,最終形成了seq_file(Sequence file:序列檔案)介面。 這個介面提供了一套簡單的函式來解決以上proc介面程式設計時存在的問題,使得程式設計更加容易,降低了Bug出現的機會。
不只是proc,在需要建立一個由一系列資料順序組合而成的虛擬檔案或一個較大的虛擬檔案時,推薦使用seq_file介面。這正是符合bin_attribute的場景。看一下seq_file的結構。
這個結構的成員與file差不多,基本不用關心,因為他們都是使用op成員定義的方法操作的,一定意義上可以看作私有成員,圖中沒有這麼畫。
需要注意的是結構裡有個private,這麼多回了應該有這個意識了,看見private就有可能跟我們結構掛上關係。事實也是如此。kernfs_open_file的指標是放到seq_file的private中的。
從file到attribute
至此只剩seq_file如何與普通的file聯絡在一起了,那就簡單了,跟大部分驅動程式是一樣的,seq_file包含在file的private_data中。整合上述關係,可以在下面類圖中表示。
從file找到sysfs的屬性檔案,經歷了很長的鏈條,這個其實是多型,由於c語言的特性,linux驅動程式用了很費勁的方式實現了多型。
上邊這個可能還是抽象一點,把ops族物件加入跟為直觀,忽略一些中間環節。
從操作上,從VFS的fs到sysfs經歷了file_operations域,kernfs_ops域和最終的sys_ops域的轉換。