sysfs是一個(gè)基于內(nèi)存的文件系統(tǒng),它的作用是將內(nèi)核信息以文件的方式提供給用戶程序使用。該文件系統(tǒng)的目錄層次結(jié)構(gòu)嚴(yán)格按照內(nèi)核的數(shù)據(jù)結(jié)構(gòu)組織。除了二進(jìn)制文件外(只有特殊場(chǎng)合才使用),sysfs文件內(nèi)容均以ASCII格式保存,且一個(gè)文件只保存一個(gè)數(shù)據(jù),另外,一個(gè)文件不可大于一個(gè)內(nèi)存頁(yè)(通常為4096字節(jié))。
sysfs提供一種機(jī)制,使得可以顯式的描述內(nèi)核對(duì)象、對(duì)象屬性及對(duì)象間關(guān)系。sysfs有兩組接口,一組針對(duì)內(nèi)核,用于將設(shè)備映射到文件系統(tǒng)中,另一組針對(duì)用戶程序,用于讀取或操作這些設(shè)備。表2描述了內(nèi)核中的sysfs要素及其在用戶空間的表現(xiàn):
sysfs在內(nèi)核中的組成要素 | 在用戶空間的顯示 |
內(nèi)核對(duì)象(kobject) | 目錄 |
對(duì)象屬性(attribute) | 文件 |
對(duì)象關(guān)系(relationship) | 鏈接(Symbolic Link) |
表2:sysfs內(nèi)部結(jié)構(gòu)與外部表現(xiàn)
在Ubuntu或Fedora等Linux系統(tǒng)中,我們可以用ls –F <路徑>命令來(lái)通過(guò)文件后綴查看文件類型?!?”表示文件夾,“@”表示鏈接,沒(méi)有后綴就是文件了。
/sys 下的子目錄 | 所包含的內(nèi)容 |
/sys/devices | 這是內(nèi)核對(duì)系統(tǒng)中所有設(shè)備的分層次表達(dá)模型,也是/sys文件系統(tǒng)管理設(shè)備的最重要的目錄結(jié)構(gòu); |
/sys/dev | 這個(gè)目錄下維護(hù)一個(gè)按字符設(shè)備和塊設(shè)備的主次號(hào)碼(major:minor)鏈接到真實(shí)的設(shè)備(/sys/devices下)的符號(hào)鏈接文件; |
/sys/bus | 這是內(nèi)核設(shè)備按總線類型分層放置的目錄結(jié)構(gòu), devices 中的所有設(shè)備都是連接于某種總線之下,在這里的每一種具體總線之下可以找到每一個(gè)具體設(shè)備的符號(hào)鏈接,它也是構(gòu)成 Linux 統(tǒng)一設(shè)備模型的一部分; |
/sys/class | 這是按照設(shè)備功能分類的設(shè)備模型,如系統(tǒng)所有輸入設(shè)備都會(huì)出現(xiàn)在/sys/class/input 之下,而不論它們是以何種總線連接到系統(tǒng)。它也是構(gòu)成Linux 統(tǒng)一設(shè)備模型的一部分; |
/sys/kernel | 這里是內(nèi)核所有可調(diào)整參數(shù)的位置,目前只有 uevent_helper, kexec_loaded, mm, 和新式的 slab 分配器等幾項(xiàng)較新的設(shè)計(jì)在使用它,其它內(nèi)核可調(diào)整參數(shù)仍然位于sysctl(/proc/sys/kernel) 接口中; |
/sys/module | 這里有系統(tǒng)中所有模塊的信息,不論這些模塊是以內(nèi)聯(lián)(inlined)方式編譯到內(nèi)核映像文件(vmlinuz)中還是編譯為外部模塊(ko文件),都可能會(huì)出現(xiàn)在/sys/module 中:
|
/sys/power | 這里是系統(tǒng)中電源選項(xiàng),這個(gè)目錄下有幾個(gè)屬性文件可以用于控制整個(gè)機(jī)器的電源狀態(tài),如可以向其中寫入控制命令讓機(jī)器關(guān)機(jī)、重啟等。 |
表3:sysfs目錄結(jié)構(gòu)
sysfs_dirent是組成sysfs單元的基本數(shù)據(jù)結(jié)構(gòu),它是sysfs文件夾或文件在內(nèi)存中的代表。sysfs_dirent只表示文件類型(文件夾/普通文件/二進(jìn)制文件/鏈接文件)及層級(jí)關(guān)系,其它信息都保存在對(duì)應(yīng)的inode中。我們創(chuàng)建或刪除一個(gè)sysfs文件或文件夾事實(shí)上只是對(duì)以sysfs_dirent為節(jié)點(diǎn)的樹(shù)的節(jié)點(diǎn)的添加或刪除。sysfs_dirent數(shù)據(jù)結(jié)構(gòu)如下:
struct sysfs_dirent {
atomic_t s_count;
atomic_t s_active;
struct sysfs_dirent *s_parent; /* 指向父節(jié)點(diǎn) */
struct sysfs_dirent *s_sibling; /* 指向兄弟節(jié)點(diǎn),兄弟節(jié)點(diǎn)是按照inode索引s_ino的大小順序鏈接在一起的。*/
const char *s_name; /* 節(jié)點(diǎn)名稱 */
union {
struct sysfs_elem_dir s_dir; /* 文件夾,s_dir->kobj指向sysfs對(duì)象 */
struct sysfs_elem_symlink s_symlink; /* 鏈接 */
struct sysfs_elem_attr s_attr; /* 普通文件 */
struct sysfs_elem_bin_attr s_bin_attr; /* 二進(jìn)制文件 */
};
unsigned int s_flags;
ino_t s_ino; /* inode索引,創(chuàng)建節(jié)點(diǎn)時(shí)被動(dòng)態(tài)申請(qǐng),通過(guò)此值和sysfs_dirent地址可以到inode散列表中獲取inode結(jié)構(gòu) */
umode_t s_mode;
struct iattr *s_iattr;
};
inode(index node)中保存了設(shè)備的主從設(shè)備號(hào)、一組文件操作函數(shù)和一組inode操作函數(shù)。文件操作比較常見(jiàn):open、read、write等。inode操作在sysfs文件系統(tǒng)中只針對(duì)文件夾實(shí)現(xiàn)了兩個(gè)函數(shù)一個(gè)是目錄下查找inode函數(shù)(.lookup=sysfs_lookup),該函數(shù)在找不到inode時(shí)會(huì)創(chuàng)建一個(gè),并用sysfs_init_inode為其賦值;另一個(gè)是設(shè)置inode屬性函數(shù)(.setattr=sysfs_setattr),該函數(shù)用于修改用戶的權(quán)限等。inode結(jié)構(gòu)如下:
struct inode {
struct hlist_node i_hash; /* 散列表鏈節(jié) */
struct list_head i_list;
struct list_head i_sb_list;
struct list_head i_dentry; /* dentry鏈節(jié) */
unsigned long i_ino; /* inode索引 */
atomic_t i_count;
unsigned int i_nlink;
uid_t i_uid;
gid_t i_gid;
dev_t i_rdev; /* 主從設(shè)備號(hào) */
const struct inode_operations *i_op; /* 一組inode操作函數(shù),可用其中l(wèi)ookup查找目錄下的inode,對(duì)應(yīng)sysfs為sysfs_lookup函數(shù) */
const struct file_operations *i_fop; /* 一組文件操作函數(shù),對(duì)于sysfs為sysfs的open/read/write等函數(shù) */
struct super_block *i_sb;
struct list_head i_devices;
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev;
};
};
dentry(directory entry)的中文名稱是目錄項(xiàng),是Linux文件系統(tǒng)中某個(gè)索引節(jié)點(diǎn)(inode)的鏈接。這個(gè)索引節(jié)點(diǎn)可以是文件,也可以是目錄。引入dentry的目的是加快文件的訪問(wèn)。dentry數(shù)據(jù)結(jié)構(gòu)如下:
struct dentry {
atomic_t d_count; /* 目錄項(xiàng)對(duì)象使用的計(jì)數(shù)器 */
unsigned int d_flags; /* 目錄項(xiàng)標(biāo)志 */
spinlock_t d_lock; /* 目錄項(xiàng)自旋鎖 */
int d_mounted; /* 對(duì)于安裝點(diǎn)而言,表示被安裝文件系統(tǒng)根項(xiàng) */
struct inode *d_inode; /* 文件索引節(jié)點(diǎn)(inode) */
/*
* The next three fields are touched by __d_lookup. Place them here
* so they all fit in a cache line.
*/
struct hlist_node d_hash; /* lookup hash list */
struct dentry *d_parent; /* parent directory */
struct qstr d_name; /* 文件名 */
/*
* d_child and d_rcu can share memory
*/
union {
struct list_head d_child; /* child of parent list */
struct rcu_head d_rcu;
} d_u;
void *d_fsdata; /* 與文件系統(tǒng)相關(guān)的數(shù)據(jù),在sysfs中指向sysfs_dirent */
unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* 存放短文件名 */
};
sysfs_dirent、inode、dentry三者關(guān)系:
圖3-1:sysfs在內(nèi)存中的形態(tài)
如上圖sysfs超級(jí)塊sysfs_sb、dentry根目錄root、sysfs_direct根目錄sysfs_root都是在sysfs初始化時(shí)創(chuàng)建。
sysfs_root下的子節(jié)點(diǎn)是添加設(shè)備對(duì)象或?qū)ο髮傩詴r(shí)調(diào)用sysfs_create_dir/ sysfs_create_file創(chuàng)建的,同時(shí)會(huì)申請(qǐng)對(duì)應(yīng)的inode的索引號(hào)s_ino。注意此時(shí)并未創(chuàng)建inode。
inode是在用到的時(shí)候調(diào)用sysfs_get_inode函數(shù)創(chuàng)建并依據(jù)sysfs_sb地址和申請(qǐng)到的s_ino索引計(jì)算散列表位置放入其中。
dentry的子節(jié)點(diǎn)也是需要用的時(shí)候才會(huì)創(chuàng)建。比如open文件時(shí),會(huì)調(diào)用path_walk根據(jù)路徑一層層的查找指定dentry,如果找不到,則創(chuàng)建一個(gè),并調(diào)用父dentry的inode的lookup函數(shù)(sysfs文件系統(tǒng)的為sysfs_lookup)查找對(duì)應(yīng)的子inode填充指定的dentry。
這里有必要介紹一下sysfs_lookup的實(shí)現(xiàn),以保證我們更加清晰地了解這個(gè)過(guò)程,函數(shù)主體如下:
static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
struct dentry *ret = NULL;
struct sysfs_dirent *parent_sd = dentry->d_parent->d_fsdata; //獲取父sysfs_direct
struct sysfs_dirent *sd;
struct inode *inode;
mutex_lock(&sysfs_mutex);
/* 在父sysfs_direct查找名為dentry->d_name.name的節(jié)點(diǎn) */
sd = sysfs_find_dirent(parent_sd, dentry->d_name.name);
/* no such entry */
if (!sd) {
ret = ERR_PTR(-ENOENT);
goto out_unlock;
}
/* 這兒就是通過(guò)sysfs_direct獲取對(duì)應(yīng)的inode,sysfs_get_inode實(shí)現(xiàn)原理上面已經(jīng)介紹過(guò)了 */
/* attach dentry and inode */
inode = sysfs_get_inode(sd);
if (!inode) {
ret = ERR_PTR(-ENOMEM);
goto out_unlock;
}
/* 填充目錄項(xiàng),至此一個(gè)目錄項(xiàng)創(chuàng)建完畢 */
/* instantiate and hash dentry */
dentry->d_op = &sysfs_dentry_ops; /* 填充目錄項(xiàng)的操作方法,該方法只提供一釋放inode函數(shù)sysfs_d_iput */
dentry->d_fsdata = sysfs_get(sd); //填充sysfs_direct
d_instantiate(dentry, inode); //填充inode
d_rehash(dentry); //將dentry加入hash表
out_unlock:
mutex_unlock(&sysfs_mutex);
return ret;
}
open的主要過(guò)程是通過(guò)指定的路徑找到對(duì)應(yīng)的dentry,并從中獲取inode,然后獲取一個(gè)空的file結(jié)構(gòu),將inode中相關(guān)內(nèi)容賦值給file,這其中包括將inode的fop賦給file的fop。因此接下來(lái)調(diào)用的filp->fop->open其實(shí)就是inode里的fop->open。新的file結(jié)構(gòu)對(duì)應(yīng)一個(gè)文件句柄fd,這會(huì)作為整個(gè)open函數(shù)的返回值。之后的read/write操作就靠這個(gè)fd找到對(duì)應(yīng)的file結(jié)構(gòu)了。
圖3-2是從網(wǎng)上找到的,清晰地描述了file和dentry以及inode之間的關(guān)系:
圖3-2:file、dentry、inode關(guān)系
進(jìn)程每打開(kāi)一個(gè)文件,就會(huì)有一個(gè)file結(jié)構(gòu)與之對(duì)應(yīng)。同一個(gè)進(jìn)程可以多次打開(kāi)同一個(gè)文件而得到多個(gè)不同的file結(jié)構(gòu),file結(jié)構(gòu)描述了被打開(kāi)文件的屬性,讀寫的偏移指針等等當(dāng)前信息。
兩個(gè)不同的file結(jié)構(gòu)可以對(duì)應(yīng)同一個(gè)dentry結(jié)構(gòu)。進(jìn)程多次打開(kāi)同一個(gè)文件時(shí),對(duì)應(yīng)的只有一個(gè)dentry結(jié)構(gòu)。dentry結(jié)構(gòu)存儲(chǔ)目錄項(xiàng)和對(duì)應(yīng)文件(inode)的信息。
在存儲(chǔ)介質(zhì)中,每個(gè)文件對(duì)應(yīng)唯一的inode結(jié)點(diǎn),但是,每個(gè)文件又可以有多個(gè)文件名。即可以通過(guò)不同的文件名訪問(wèn)同一個(gè)文件。這里多個(gè)文件名對(duì)應(yīng)一個(gè)文件的關(guān)系在數(shù)據(jù)結(jié)構(gòu)中表示就是dentry和inode的關(guān)系。
inode中不存儲(chǔ)文件的名字,它只存儲(chǔ)節(jié)點(diǎn)號(hào);而dentry則保存有名字和與其對(duì)應(yīng)的節(jié)點(diǎn)號(hào),所以就可以通過(guò)不同的dentry訪問(wèn)同一個(gè)inode。
sysfs與普通文件系統(tǒng)的最大差異是,sysfs不會(huì)申請(qǐng)任何內(nèi)存空間來(lái)保存文件的內(nèi)容。事實(shí)上再不對(duì)文件操作時(shí),文件是不存在的。只有用戶讀或?qū)懳募r(shí),sysfs才會(huì)申請(qǐng)一頁(yè)內(nèi)存(只有一頁(yè)),用于保存將要讀取的文件信息。如果作讀操作,sysfs就會(huì)調(diào)用文件的父對(duì)象(文件夾kobject)的屬性處理函數(shù)kobject->ktype->sysfs_ops->show,然后通過(guò)show函數(shù)來(lái)調(diào)用包含該對(duì)象的外層設(shè)備(或驅(qū)動(dòng)、總線等)的屬性的show函數(shù)來(lái)獲取硬件設(shè)備的對(duì)應(yīng)屬性值,然后將該值拷貝到用戶空間的buff,這樣就完成了讀操作。寫操作也類似,都要進(jìn)行內(nèi)核空間?à用戶空間內(nèi)存的拷貝,以保護(hù)內(nèi)核代碼的安全運(yùn)行。
圖3-1為用戶空間程序讀sysfs文件的處理流程,其他操作類似:
圖3-3:用戶空間程序讀sysfs文件的處理流程
聯(lián)系客服