 |
 |
培訓(xùn)信息 |
|
|
|
 |
 |
贊助商 |
|
|
|
|
 |
 |
proc 文件系統(tǒng)分析(二)(轉(zhuǎn)載) |
|
|
| proc 文件系統(tǒng)分析(二)(轉(zhuǎn)載) |
| 作者:佚名 來(lái)源:單片機(jī) 錄入:jdzj868 更新時(shí)間:2009-8-12 16:53:36 點(diǎn)擊數(shù):0 |
【字體:
】 |
| | 原文地址:http://www.linuxforum.net/forum/showthreaded.php?Cat=&Board=linuxK&Number=199529&Search=true&Forum=l作者: | | zmwillow (journeyman)不知正確與否。 這樣是表示對(duì)作者的尊重。 如果有不對(duì),請(qǐng)通知我,我將即時(shí)更改. | | 二 proc文件系統(tǒng)分析 根據(jù)前面的分析,我們可以基本確定對(duì)proc文件系統(tǒng)的分析步驟。我將按照proc文件系統(tǒng)注冊(cè),安裝的順序?qū)ζ溥M(jìn)行分析,然后基于代碼,對(duì)proc文件系統(tǒng)的結(jié)構(gòu)進(jìn)行分析,尤其是proc文件系統(tǒng)用于內(nèi)部管理的數(shù)據(jù)結(jié)構(gòu)。最后,我們將根據(jù)分析結(jié)果,提出可行的xml封裝計(jì)劃。 在對(duì)proc文件系統(tǒng)的數(shù)據(jù)結(jié)構(gòu)的分析中,我將把重點(diǎn)放在數(shù)據(jù)輸出的分析上,它是提出一種標(biāo)準(zhǔn)的XML封裝方法的基礎(chǔ)。 (一) Linux 相關(guān)源代碼簡(jiǎn)介 在linux代碼樹(shù)中,所有文件系統(tǒng)的代碼都放在linux/fs/目錄中,其中,proc文件系統(tǒng)的源代碼在linux/fs/proc中,下面我簡(jiǎn)單介紹一下proc目錄中的源文件。 在目錄中共有11個(gè)相關(guān)文件,它們是: procfs_syms.c inode.c generic.c base.c array.c root.c proc_tty.c proc_misc.c kmsg.c kcore.c proc_devtree.c 其中,procfs_syms.c,generic.c以及inode.c與proc文件系統(tǒng)的管理相關(guān),包括proc文件系統(tǒng)的注冊(cè),以及向內(nèi)核其他子系統(tǒng)提供的例程等等,這是最重要的一部分代碼,我們將從這里開(kāi)始對(duì)proc文件系統(tǒng)進(jìn)行分析。 源文件root.c與proc文件系統(tǒng)的根結(jié)點(diǎn)的管理相關(guān)。 而base.c,array.c則用來(lái)處理/proc目錄中進(jìn)程的信息,包括命令行,進(jìn)程狀態(tài),內(nèi)存狀態(tài)等等與進(jìn)程相關(guān)的內(nèi)容。proc_tty.c用來(lái)處理/proc/tty信息,proc_misc.c則用來(lái)管理與/proc目錄中的大多數(shù)文件。 除此之外,還有兩個(gè)非常重要的頭文件proc_fs.h,proc_fs_i.h,我們可以在/linux/include/linux/目錄中找到。 (二) proc文件系統(tǒng)的注冊(cè) proc文件系統(tǒng)遵循VFS的規(guī)范,因此在使用之前,必須進(jìn)行注冊(cè)。我們知道,每一個(gè)文件系統(tǒng),都會(huì)在自己的初始化例程中填寫(xiě)一個(gè) file_system_type 的數(shù)據(jù)結(jié)構(gòu),然后調(diào)用注冊(cè)函數(shù)register_filesystem(struct file_system_type *fs) 進(jìn)行注冊(cè)。 proc文件系統(tǒng)中與之相關(guān)的文件是procfs_syms.c,在該文件中,聲明了proc文件系統(tǒng)的類(lèi)型: static DECLARE_FSTYPE(proc_fs_type, "proc", proc_read_super, FS_SINGLE); 而我們?cè)?fs.h 中可以找到宏DECLARE_FSTYPE的定義: #define DECLARE_FSTYPE(var,type,read,flags) \ struct file_system_type var = { \ name: type, \ read_super: read, \ fs_flags: flags, \ owner: THIS_MODULE, \ } 因此我們可以看到,我們聲明了一個(gè)文件類(lèi)型proc_fs_type,它的名字是“proc”,讀取超級(jí)塊的函數(shù)是proc_read_super,fs_flags設(shè)置為FS_SINGLE,根據(jù)源碼中的說(shuō)明,我們知道,當(dāng)文件系統(tǒng)的fs_flags聲明為FS_SINGLE時(shí),說(shuō)明文件系統(tǒng)只有一個(gè)超級(jí)塊,并且,必須在注冊(cè)函數(shù)之后調(diào)用kern_mount(),使得在內(nèi)核范圍內(nèi)的vfsmnt被放置在->kern_mnt處。 下面就是proc文件系統(tǒng)的注冊(cè),函數(shù)init_proc_fs()的代碼如下所示: static int __init init_proc_fs(void) { int err = register_filesystem(&proc_fs_type); if (!err) { proc_mnt = kern_mount(&proc_fs_type); err = PTR_ERR(proc_mnt); if (IS_ERR(proc_mnt)) unregister_filesystem(&proc_fs_type); else err = 0; } return err; } 可以看到,proc文件系統(tǒng)的注冊(cè)非常簡(jiǎn)單,主要有如下幾個(gè)步驟: 1.調(diào)用register_filesystem(&proc_fs_type),用一個(gè)非常巧妙的方法將proc文件類(lèi)型加入到文件類(lèi)型的單向鏈表中,如果發(fā)生錯(cuò)誤,則返回。 2.調(diào)用kern_mount函數(shù),該函數(shù)基本完成三個(gè)步驟,首先調(diào)用read_super()函數(shù),在這個(gè)函數(shù)里,VFS將為proc文件系統(tǒng)分配一個(gè)超級(jí)塊結(jié)構(gòu),并設(shè)置s_dev,s_flags等域,然后,將調(diào)用proc文件系統(tǒng)的自己的read_super例程,對(duì)應(yīng)proc文件系統(tǒng),該例程是proc_read_super(),該例程將設(shè)置超級(jí)塊結(jié)構(gòu)的其他值。我們將在下一節(jié)進(jìn)行分析。 其次,使用add_vfsmnt()函數(shù)建立proc文件系統(tǒng)的vfsmount結(jié)構(gòu),并將其加入到已裝載文件系統(tǒng)的鏈表中(可參考圖-xx)。 最后,返回該vfsmount結(jié)構(gòu),并利用返回值,使用指針proc_mnt指向該vfsmount結(jié)構(gòu)。 3.判斷返回值是否錯(cuò)誤,如果錯(cuò)誤,那么就卸載文件系統(tǒng)。 這樣,一個(gè)文件系統(tǒng)就成功注冊(cè)到核心了。同樣,proc文件系統(tǒng)的卸載,也非常簡(jiǎn)單,代碼如下: static void __exit exit_proc_fs(void) { unregister_filesystem(&proc_fs_type); kern_umount(proc_mnt); } (三) 建立proc文件系統(tǒng)的超級(jí)塊 我們剛才看到,在kern_mount函數(shù)中,調(diào)用read_proc建立了超級(jí)塊結(jié)構(gòu),然后就會(huì)調(diào)用文件系統(tǒng)自己提供的讀取超級(jí)塊的例程,用來(lái)填充自己的超級(jí)塊結(jié)構(gòu),下面我們看一下proc文件系統(tǒng)的超級(jí)塊讀取例程proc_read_super()是如何工作的,以及它最終完成了哪些工作,該函數(shù)在fs/proc/inode.c中實(shí)現(xiàn): struct super_block *proc_read_super(struct super_block *s,void *data, int silent) { struct inode * root_inode; struct task_struct *p;
s->s_blocksize = 1024; s->s_blocksize_bits = 10; s->s_magic = PROC_SUPER_MAGIC; s->s_op = &proc_sops; s->s_maxbytes = MAX_NON_LFS; root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root);
if (!root_inode) goto out_no_root; /* * Fixup the root inode's nlink value */ read_lock(&tasklist_lock); for_each_task(p) if (p->pid) root_inode->i_nlink++; read_unlock(&tasklist_lock); s->s_root = d_alloc_root(root_inode); if (!s->s_root) goto out_no_root; parse_options(data, &root_inode->i_uid, &root_inode->i_gid); return s;
out_no_root: printk("proc_read_super: get root inode failed\n"); iput(root_inode); return NULL; } 該函數(shù)進(jìn)行了如下幾步操作: 1.在該函數(shù)里,首先向作為參數(shù)傳入的超級(jí)塊寫(xiě)入文件系統(tǒng)的基本信息,s_blocksize設(shè)置為1024,由于1024=2^10,因此,s_blocksize_bit設(shè)置為10,然后是proc文件系統(tǒng)的魔數(shù),為PROC_SUPER_MAGIC。超級(jí)塊的函數(shù)集設(shè)置為proc_sops,對(duì)于proc文件系統(tǒng)來(lái)講,只實(shí)現(xiàn)了4個(gè)超級(jí)塊函數(shù),我們將在后面進(jìn)行分析。然后,設(shè)置proc文件系統(tǒng)中的文件最大字節(jié)數(shù)為MAX_NON_LFS,在fs.h中,定義這個(gè)宏為 ((1UL<<31)-1) 。 2.使用proc_get_inode(s, PROC_ROOT_INO, &proc_root) 函數(shù)建立根結(jié)點(diǎn)root_inode。這個(gè)函數(shù)根據(jù)參數(shù)ino,來(lái)得到inode,我們后面將進(jìn)一步分析該函數(shù)。如果沒(méi)有得到根結(jié)點(diǎn),則跳到out_no_root標(biāo)號(hào),并退出。參數(shù)ino用來(lái)標(biāo)志inode,在建立proc的新索引節(jié)點(diǎn)的時(shí)候,會(huì)動(dòng)態(tài)地分配它一個(gè)ino。 3.修正root_inode的鏈接數(shù)。它將遍歷進(jìn)程鏈表,對(duì)于每一個(gè)進(jìn)程,都使i_nlink++,這是因?yàn)樵?proc目錄中,每一個(gè)進(jìn)程都有一個(gè)目錄,換句話(huà)說(shuō),存在一個(gè)進(jìn)程,就必然在proc目錄中對(duì)應(yīng)一個(gè)子目錄,因此在建立proc的根節(jié)點(diǎn)時(shí),要根據(jù)進(jìn)程數(shù)修改它的i_nlink。 4.根據(jù)剛剛建立的root_inode,為超級(jí)塊的s_root建立root dentry: s->s_root = d_alloc_root(root_inode) 其中root_inode 的類(lèi)型是struct inode *, 而s_root的類(lèi)型是struct dentry *。我們?cè)诮榻BVFS的時(shí)候知道,目錄高速緩存以樹(shù)狀結(jié)構(gòu)存在,因此,在建立文件系統(tǒng)的根結(jié)點(diǎn)后,需要使用d_alloc_root()函數(shù)建立一個(gè)根目錄(root dentry),也就是說(shuō),該dentry結(jié)構(gòu)的。 最終成功返回超級(jí)塊,這時(shí),超級(jí)塊已經(jīng)填上了必要的數(shù)據(jù)信息。因此可以看到,超級(jí)塊讀取例程主要完成了兩部分的工作,首先向超級(jí)塊寫(xiě)入必要的數(shù)據(jù),其次建立了該文件系統(tǒng)的根結(jié)點(diǎn),并在目錄高速緩存中建立了相應(yīng)的dentry結(jié)構(gòu)。 (四) proc文件系統(tǒng)超級(jí)塊的操作函數(shù)集 在上一節(jié)我們看到了proc文件系統(tǒng)如何設(shè)置自己的超級(jí)塊,并且將超級(jí)塊操作函數(shù)集設(shè)置為proc_sops,這一節(jié)我們就分析一下,對(duì)于proc文件系統(tǒng)的超級(jí)塊,需要提供什么操作,以及如何實(shí)現(xiàn)這些操作。 在文件fs/proc/inode.c中,有如下定義: static struct super_operations proc_sops = { read_inode: proc_read_inode, put_inode: force_delete, delete_inode: proc_delete_inode, statfs: proc_statfs, }; 我們可以看到,proc文件系統(tǒng)僅僅實(shí)現(xiàn)了4個(gè)超級(jí)塊操作函數(shù)。它使用了一種比較特殊的方法來(lái)初始化結(jié)構(gòu),這種方法叫作labeled elements,這是GNU的C擴(kuò)展,這樣在初始化結(jié)構(gòu)時(shí),不必按照結(jié)構(gòu)的順序,只要指明域名,就可初始化其值,而對(duì)于沒(méi)有提到的域,將自動(dòng)設(shè)置為0。 所以我們看到,proc文件系統(tǒng)僅僅定義了4個(gè)超級(jí)塊操作函數(shù),我們看一下為什么其他的操作函數(shù)不必定義。 首先,我們知道,proc文件系統(tǒng)僅僅存在于內(nèi)存中,并不需要物理設(shè)備,因此write_inode函數(shù)就不需要定義了。而函數(shù)notify_change,在索引節(jié)點(diǎn)的屬性被改變的時(shí)候會(huì)被調(diào)用,而對(duì)于proc文件系統(tǒng)的inode來(lái)說(shuō),并未提供setattr 函數(shù),換句話(huà)說(shuō),文件的屬性不會(huì)被改變,所以,notif_change也就不會(huì)被調(diào)用(proc文件系統(tǒng)對(duì)于inode_operations,同樣僅僅提供了很少的幾種操作,并且,在建立文件樹(shù)的時(shí)候,還針對(duì)不同的文件/目錄,設(shè)置了不同的索引節(jié)點(diǎn)操作函數(shù),這將在以后進(jìn)行詳細(xì)的介紹);陬(lèi)似的原因,其他的函數(shù),諸如put_super,write_super,以及clear_inode等等函數(shù),都沒(méi)有進(jìn)行定義。 下面我們看一下定義的這4個(gè)函數(shù): 1 read_inode: proc_read_inode 這個(gè)函數(shù)用來(lái)從已裝載文件系統(tǒng)中,讀取指定索引節(jié)點(diǎn)的信息。實(shí)際上,在需要讀取特定的索引節(jié)點(diǎn)時(shí),會(huì)調(diào)用VFS的iget(sb, ino)函數(shù),其中,sb指定了文件系統(tǒng)的超級(jí)塊,而ino是索引節(jié)點(diǎn)的標(biāo)號(hào)。這個(gè)函數(shù)會(huì)在該超級(jí)塊的dcache中尋找該索引節(jié)點(diǎn),如果找到,則返回索引節(jié)點(diǎn),否則,就必須從邏輯文件系統(tǒng)中讀取指定的索引節(jié)點(diǎn),這時(shí),會(huì)調(diào)用get_new_inode()函數(shù),在這個(gè)函數(shù)里,會(huì)分配一個(gè)inode結(jié)構(gòu),填寫(xiě)一些基本的信息,然后,就會(huì)調(diào)用超級(jí)塊的操作函數(shù)read_inode,對(duì)于proc文件系統(tǒng)而言,就是proc_read_inode()函數(shù)。 在后面的介紹里我們會(huì)知道,proc文件系統(tǒng)為了方便自己對(duì)文件的管理,對(duì)于每一個(gè)已經(jīng)注冊(cè)的proc文件,都建立并維護(hù)了一個(gè)的proc_dir_entry結(jié)構(gòu)。這個(gè)結(jié)構(gòu)非常的重要,對(duì)于proc文件系統(tǒng)來(lái)說(shuō),這個(gè)結(jié)構(gòu)是自己的私有數(shù)據(jù),相當(dāng)于其他邏輯文件系統(tǒng)(比如ext2文件系統(tǒng))在物理硬盤(pán)上的索引節(jié)點(diǎn)。因此,只有在必要的時(shí)候,才會(huì)把proc文件系統(tǒng)的proc_dir_entry結(jié)構(gòu)鏈接到VFS的索引節(jié)點(diǎn)中。 因此,proc_read_inode函數(shù)的主要目的,是建立一個(gè)新的索引節(jié)點(diǎn),只需填充一些基本的信息即可。所以我們可以看到proc_read_inode函數(shù)非常的簡(jiǎn)單: static void proc_read_inode(struct inode * inode) { inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; } 要說(shuō)明的是,在調(diào)用proc_read_inode函數(shù)之前,VFS的get_new_inode()函數(shù)已經(jīng)為inode設(shè)置了其他的基本信息,比如i_sb,i_dev,i_ino,i_flags 以及i_count等等。 2 put_inode: force_delete put_inode函數(shù)是在索引節(jié)點(diǎn)的引用計(jì)數(shù)減少的時(shí)候調(diào)用,我們看到,proc文件系統(tǒng)沒(méi)有實(shí)現(xiàn)自己的put_inode函數(shù),而是簡(jiǎn)單地設(shè)置了VFS的force_delete 函數(shù),我們看一下這個(gè)函數(shù)的內(nèi)容: void force_delete(struct inode *inode) { /* * Kill off unused inodes ... iput() will unhash and * delete the inode if we set i_nlink to zero. */ if (atomic_read(&inode->i_count) == 1) inode->i_nlink = 0; } 我們知道,put_inode函數(shù)是在引用計(jì)數(shù)i_count減少之前調(diào)用的,因此,對(duì)于proc文件系統(tǒng)來(lái)說(shuō),在每一次inode引用計(jì)數(shù)減少之前,都要檢查引用計(jì)數(shù)會(huì)不會(huì)減少至零,如果是,那么就將改索引節(jié)點(diǎn)的鏈接數(shù)直接設(shè)置為零。 3 delete_inode: proc_delete_inode 當(dāng)一個(gè)索引節(jié)點(diǎn)的引用計(jì)數(shù)和鏈接數(shù)都到零的時(shí)候,會(huì)調(diào)用超級(jí)塊的delete_inode函數(shù)。由于我們使用force_delete實(shí)現(xiàn)了proc超級(jí)塊的put_inode方法,因此我們知道,對(duì)于proc文件系統(tǒng)來(lái)說(shuō),當(dāng)一個(gè)inode的引用計(jì)數(shù)為零的時(shí)候,它的鏈接數(shù)也必為零。 我們看一下該函數(shù)的源碼: /* * Decrement the use count of the proc_dir_entry. */ static void proc_delete_inode(struct inode *inode) { struct proc_dir_entry *de = inode->u.generic_ip;/* for the procfs, inode->u.generic_ip is a 'proc_dir_entry' */ inode->i_state = I_CLEAR; if (PROC_INODE_PROPER(inode)) { proc_pid_delete_inode(inode); return; } if (de) { if (de->owner) __MOD_DEC_USE_COUNT(de->owner); de_put(de); } } 我們看到,這個(gè)函數(shù)基本上做了三個(gè)工作,首先,將這個(gè)索引節(jié)點(diǎn)的狀態(tài)位設(shè)置為I_CLEAR,這標(biāo)志著,這個(gè)inode結(jié)構(gòu)已經(jīng)不再使用了。其次,根據(jù)這個(gè)索引節(jié)點(diǎn)的ino號(hào),檢查它是否是pid目錄中的索引節(jié)點(diǎn),因?yàn)閜id目錄的索引節(jié)點(diǎn)號(hào)使用 #define fake_ino(pid,ino) (((pid)<<16)|(ino)) 定義,因此,檢查條件為if (PROC_INODE_PROPER(inode)) 。我們知道,/proc目錄中有許多于進(jìn)程相關(guān)的目錄和文件,這些proc文件是另一種組織形式,它們的文件和進(jìn)程密切相關(guān),不是像其他proc文件那樣使用create_proc_entry函數(shù)注冊(cè)的,而是根據(jù)實(shí)際的進(jìn)程鏈表動(dòng)態(tài)生成的,因此,可能在以后的linux版本中,會(huì)單獨(dú)為pid proc文件建立一個(gè)超級(jí)塊,但目前的情況下,使用inode的ino來(lái)區(qū)分。由于pid目錄中的inode和進(jìn)程數(shù)據(jù)結(jié)構(gòu)密切相關(guān),因此,當(dāng)要釋放inode的時(shí)候,要做一些特殊的工作,釋放一些特殊的資源。這部分工作由proc_pid_delete_inode()函數(shù)完成。 最后,調(diào)用de_put() 函數(shù),對(duì)與此inode相關(guān)聯(lián)的proc_dir_entry結(jié)構(gòu)進(jìn)行必要的操作。即減少proc_dir_entry的引用計(jì)數(shù),如果計(jì)數(shù)到達(dá)零,則釋放該proc_dir_entry結(jié)構(gòu)。 4 statfs: proc_statfs 我們?cè)诜治鯲FS的時(shí)候知道,statfs函數(shù)是為了實(shí)現(xiàn)系統(tǒng)調(diào)用statfs(2)。我們看一下它的源代碼: static int proc_statfs(struct super_block *sb, struct statfs *buf) { buf->f_type = PROC_SUPER_MAGIC; /* here use the super_block's s_magic ! */ buf->f_bsize = PAGE_SIZE/sizeof(long); /* optimal transfer block size */ buf->f_bfree = 0; /* free blocks in fs */ buf->f_bavail = 0; /* free blocks avail to non-superuser */ buf->f_ffree = 0; /* free file nodes in fs */ buf->f_namelen = NAME_MAX; /* maximum length of filenames */ return 0; } 我們看到,它將文件系統(tǒng)的統(tǒng)計(jì)數(shù)據(jù)填充到一個(gè)buf中,文件系統(tǒng)類(lèi)型為PROC_SUPER_MAGIC,在文件系統(tǒng)中的空閑塊以及文件系統(tǒng)中的文件節(jié)點(diǎn)都設(shè)置為0,因此對(duì)于只存在于內(nèi)存中的proc文件系統(tǒng)來(lái)說(shuō),這些統(tǒng)計(jì)數(shù)據(jù)是沒(méi)有意義的。 | 原文地址 http://www.linuxforum.net/forum/showflat.php?Cat=&Board=linuxK&Number=200166&page=82&view=collapsed& |
|
|
發(fā)表評(píng)論 告訴好友 打印此文 收藏此頁(yè) 關(guān)閉窗口 返回頂部 |
|
|
 |
 |
網(wǎng)友評(píng)論:(只顯示最新5條。) |
|
|
|
|
|