核心sys檔案系統學習
阿新 • • 發佈:2019-02-11
學習Linux那些事兒之我是Sysfs筆記
sys檔案系統的作用:用於將系統中的裝置組織成層次結構,並向用戶模式程式提供詳細的核心資料結構資訊。 sysfs的掛載過程:mount -t sysfs sysfs /sys,sysfs是個特殊檔案系統,它沒有實際存放檔案的介質,斷電就沒有了。sysfs資訊來源是 kobject層次結構,讀一個sysfs檔案,就是動態從kobject結構提取資訊,生成檔案。 Kobject: (1)核心中用struct kobject表示,該結構體使裝置在底層都具有統一的介面,kobject提供基本物件的管理。 (2)每個在核心中註冊的kobject物件都對應於sysfs檔案系統的一個目錄。 (3)kobject是組成裝置模型的基本結構,通過kobject將所有裝置連線起來,形成一個樹狀結構,該結構就與/sys/對應。 kobject為一些大的資料結構和子系統提供基本的物件管理: (1)物件引用計數 (2)維護物件連結串列(集合) (3)物件上鎖 (4)在使用者空間表示 kobject的結構表示:操作該結構體的介面: (1)增減物件引用計數的,kobject_get()、kobject_put()。 (2)對kobject初始化,kobject_init()。 (3)設定kobject的名稱,kobject_set_name()。 (4)釋放kobject物件,kobject_release()。 (5)將kobject新增到linux裝置層次,kobject_add()。 (6)kobject註冊函式,kobject_register,先呼叫kobject_init(),在呼叫kobject_add。 成員kobj_type
(1)release方法用於釋放kobject佔用的資源。 (2)sysfs_ops指向sysfs操作表和一個sysfs檔案系統預設屬性列表。操作表包括的函式store()和show(), 當用戶讀取屬性時,show被呼叫,該函式將屬性值存入buffer返回給使用者態。store用於儲存使用者態傳入的屬性值。 成員attribute
它以檔案的形式輸出到sysfs的目錄中,kobject對應的目錄下面,檔名就是name。 kset的作用:建立上層(sub_system)和下層(kobject)的關聯性,kobject利用它分別自己屬於哪個型別,然後在/sys/下建立正確的目錄位置。
(1)包含在kset中的所有kobject被組織成一個雙向迴圈連結串列,list域正式改連結串列的頭。 (2)ktype 指向一個kobj type結構,被該kset中的所有kobject共享,表示這些物件的型別。 (3)kobject,所有屬於該kset的kobject物件的parent域都指向這個內嵌的物件。 (4)kset還依賴kobj維護的引用計數。該實際計數就是內嵌kobject物件的引用計數。
kset操作的相關函式: (1)kset_init(),完成指定kset的初始化。 (2)kset_get和kset_put分別是增加和減少kset物件的引用計數。 (3)kset_add和kset_del分別實現指定kset物件加入裝置層次和從其中刪除。 (4)kset_register和kset_unregister分別完成kset的註冊和登出。 subsystem kset是管理kobject的集合,subsystem是管理kset的集合,它描述系統中某一類裝置子系統,若block subsys表示所有的塊裝置 ,對應於sysfs檔案系統的block目錄。
該結構體的描述 (1)subsystem與kset的區別是多了一個訊號量,每個kset屬於某個subsystem。 (2)通過設定kset結構的subsys域指向指定的subsystem,可以將一個kset加入該subsystem。 (3)所有掛接到同一subsystem的kset共享同一個rwsem訊號量,用於同步訪問kset中的連結串列。 也有一組相關操作函式 subsystem_init、subsystem_register、subsystem_unregister。 裝置模型的上層容器 系統中匯流排由struct bus_type描述
(1)每個bus_type物件都內嵌一個subsystem物件,bus_type物件管理系統中所有匯流排型別的subsystem物件。 (2)每個bus_type物件對應於/sys/bus/目錄的一個字幕了,如pci匯流排,對應/sys/bus/pci。 device 系統中的任一裝置在裝置模型中都由一個device物件描述,對應結構體struct device
( 1 ) g_list將該device物件掛接到全域性裝置連結串列中,所有的device物件都包含在devices subsys中,並組織成層次結構。 ( 2 ) node將該物件掛接到其兄弟物件的連結串列中。 ( 3 ) bus_list用於將連線到相同總線上的裝置組織成連結串列。 ( 4 ) driver_list將統一驅動程式管理的所有裝置組織為連結串列。 ( 5 ) children指向該device物件子物件連結串列頭。 ( 6 ) parent指向父物件。 ( 7 ) 內嵌一個kobject物件,用於引用計數管理並通過它實現裝置層次結構。 ( 8 ) driver指向管理該裝置的驅動程式物件。 ( 9 ) driver_data提供給驅動程式的資料。 ( 10 ) bus域描述裝置鎖連線的匯流排型別。 driver 系統中的每個驅動程式由一個device_driver物件描述
kobject物件實現引用計數管理和層次結構組織。 檔案系統 sysfs與Kobject的關聯:kobject_add->create_dir()->sysfs_create_dir() bus_createfile->sysfs_create_file,sysfs利用vfs的介面去讀寫kobject的層次結構,建立起來的檔案系統。 VFS:linux把所有的資源都看成檔案,讓使用者通過一個統一的檔案系統操作,也就是同一組系統呼叫。對不同檔案系統的檔案進行操作。 這樣就能隱藏不同檔案系統的實現細節,為使用者程式提供統一的抽閒的檔案系統。 VFS操作的系列介面: (1)file_operations 對每個具體檔案的讀寫操作。 (2)dentry_operations,inode_operations對檔案的屬性,如改名字,建立或刪除操作。 struct file_operations { ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); int (*open) (struct inode *, struct file *); }; struct dentry_operations { ... }; struct inode_operations { int (*create) (struct inode *,struct dentry *,int, struct nameidata *); struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *); int (*link) (struct dentry *,struct inode *,struct dentry *); int (*mkdir) (struct inode *,struct dentry *,int); int (*rmdir) (struct inode *,struct dentry *); ...} open一個檔案的呼叫流程,不同的檔案系統,對應不同過的file_operations->open(): open->sys_open() -> filp_open()-> dentry_open() -> file_operations->open() Dentry與inode Dentry是表示目錄項,inode表示索引節點。
檔案:按一定的形式儲存在介質上的資訊,所以一個檔案包含儲存資料本身和該檔案的組織和管理資訊。記憶體中每個檔案都有一個dentry和inode。 dentry:記錄檔名,上級目錄等資訊,通過d_parent和d_child連線起來, 用於形成樹狀結構。 indoe:記錄有關該檔案的組織和管理的資訊,記錄檔案在儲存介質的位置與分佈。通過inode,可以找到一個數組,該陣列記錄檔案內容的位置。 檔案分為:磁碟檔案,裝置檔案,特殊檔案。 磁碟檔案:dentry和inode的載體都儲存在磁碟上,斷電還在,初始化,通過讀取磁碟的內容組成檔案樹。 特殊檔案:在內容中有inode和dentry結構體,不一定有儲存介質,斷電就沒有了。當讀一個特殊檔案時,讀出來的資料是系統內部按一定規則生成的,或從記憶體中收集,加工出來的。如 sysfs。 程序開啟一個檔案,實際是在內容建立檔案的dentry和inode結構,並讓它與程序結構聯絡起來。結構如圖:
Sysfs檔案系統模型 sysfs的檔案系統的所讀寫的資訊存放在kobject中。 sysfs檔案系統有自己的dirent,一個dentry對應一個dirent,struct sysfs_dirent,該結構體是kobject和sysfs聯絡的中間連線結構。 dentry->d_fsdata是個void指標,指向sysfs_dirent結構。 struct sysfs_dirent { atomic_t s_count; struct list_head s_sibling; struct list_head s_children; void * s_element; int s_type; umode_t s_mode; struct dentry * s_dentry; struct iattr * s_iattr; atomic_t s_event; }; 通過s_sibling,s_children連線成一個層次結構,而且它的層次結構與sysfs完全一致,就是一個連線kobject和dentry結構的連線件。 舉例: /sys/bus/ldd/ |--device |--driver `--version dentry、dirent、kobject的連線圖
dentry->d_fsdata = &dirent; dirent->element = &kobject; kobject->dentry = &dentry; Sys建立目錄 新增一個kobject結構時,在/sys/下建立一個新目錄: kobject_add() -> create_dir() -> sysfs_create_dir() (1)sysfs_get_dentry() 根據父結點和當前想建立目錄的檔名得到dentry結構,先在快取找,找不到,就用d_alloc新建一個dentry結構。 (2)sysfs_ctreate() sysfs_create()->sysfs_new_inode(mode) -> new_inode(sysfs_sb) 建立一個新的索引節點。 (3)sysfs_make_dirent 得到一個dirent結構,再把它連線到上層目錄的sysfs_dirent的s_children連結串列。sysfs_make_dirent為新建的dentry建立一個dirent結構, 並將dentry和dirent聯絡起來。 (4)總結
Sys建立普通檔案 普通檔案,對應於kobject中的屬性,用sysfs_create_file。 sysfs_create_file -> sysfs_ad_file -> sysfs_make_dirent 在sysfs_make_dirent建立一個sysfs_dirent,最後在sysfs_lookup()->sysfs_attach_attr()中穿件inode結構。 Sys 讀入資料夾內容 新建資料夾的時候 inode->i_op = &sysfs_dir_inode_operations; inode->i_fop = &sysfs_dir_operations; struct file_operations sysfs_dir_operations = { .open = sysfs_dir_open, .release = sysfs_dir_close, .llseek = sysfs_dir_lseek, .read = generic_read_dir, .readdir = sysfs_readdir, }; int main(){ DIR * dir; struct dirent *ptr; dir = opendir("/sys/bus/"); while((ptr = readdir(dir))!=NULL){ printf("d_name :%s ",ptr->d_name); } closedir(dir); return -1; } (1)sysfs_dir_open openddir->sys_open->file_open->dentry_open->sysfs_dir_open 核心空間新建一個dirent結構,連入父輩的dentry,將它地址儲存在file->private_data中。 使用者空間新建一個DIR結構, #define __dirstream DIR struct __dirstream { int fd; /* File descriptor. */ char *data; /* Directory block. */ size_t allocation; /* Space allocated for the block. */ size_t size; /* Total valid data in the block. */ size_t offset; /* Current offset into the block. */ off_t filepos; /* Position of next entry to read. */ __libc_lock_define (, lock) /* Mutex lock for this structure. */ }; (2)sysfs_readdir() readdir(dir) -> getdents() ->系統呼叫->sys32_readdir() -> vfs_readdir() -> sysfs_readdir() 從sys_dir_open建立的sysfs_dirent開始,遍歷當前dentry->dirent下的所有子sysfs_dirent結構,讀出名字,再回調filldir將檔名檔案型別資訊 按照一定個是寫入某個緩衝區。 Sys 讀入普通檔案內容 int main(){ char *name = "/sys/bus/ldd/version"; char buf[500]; int fd; int size; fd = open(name, O_RDONLY); printf("fd:%d ",fd); size = read(fd,buf,sizeof(buf)); printf("size:%d ",size); printf("%s",buf); close(fd); return -1; } (1)sysfs_open_file open() ->系統呼叫-> sys_open() -> filp_open()-> dentry_open() -> sysfs_open_file() 檢查一下許可權,建立一個sysfs緩衝區sysfs_buffer buffer,這裡buffer->ops設定成bus_sysfs_ops,讓file->private_data = buffer。 (2)sysfs_read_file read()->系統呼叫 sys_read() -> vfs_read() -> sysfs_read_file() sysfs_read_file() ---> fill_read_buffer() ---> sysfs_buffer->bus_sysfs_ops->bus_attr_show() ---> bus_attribute->show_bus_version() fill_read_buffer是真正的讀,把內容讀到sysfs定義的緩衝區sysfs_buffer。 flush_read_buffer是把緩衝區copy到使用者空間。