• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            小默

            【轉】proc文件系統分析(四)

            (六) 對proc文件默認操作的分析 
            現在,我們已經基本清楚了proc文件系統對自己proc_dir_entry結構的管理了。下面我們回過頭來,再看一下在文件注冊函數中的一段代碼: 
            if (S_ISDIR(dp->mode)) { 
            if (dp->proc_iops == NULL) { 
            dp->proc_fops = &proc_dir_operations; 
            dp->proc_iops = &proc_dir_inode_operations; 
            } 
            dir->nlink++; 
            } else if (S_ISLNK(dp->mode)) { 
            if (dp->proc_iops == NULL) 
            dp->proc_iops = &proc_link_inode_operations; 
            } else if (S_ISREG(dp->mode)) { 
            if (dp->proc_fops == NULL) 
            dp->proc_fops = &proc_file_operations; 
            } 
            我在前面已經提過,這段代碼根據注冊的proc文件類型的不同,為proc_dir_entry結構設置了不同的操作函數集。也就是說,我們使用封裝的create_proc_entry函數在proc文件系統中注冊文件時,可以不用去管這些操作函數集,因為該結構總是自動地設置了相應的proc_iops和proc_fops操作函數。下面我們就對這些默認的操作進行一個分析,因為這對我們了解proc文件系統和VFS的結構非常重要。 
            1 對普通文件的操作 
            我們首先看一下普通proc文件的函數集,根據代碼段: 
            if (S_ISREG(dp->mode)) { 
            if (dp->proc_fops == NULL) 
            dp->proc_fops = &proc_file_operations; 
            } 
            我們可以看到,對于普通的proc文件,只設置了文件操作,即proc_file_operations,從這一點上可以看出,對于普通的proc文件,只缺省提供了文件操作,因此,在必要的時候,我們必須手工設置需要的索引節點操作函數集,比如inode_operations中的權限檢查函數permission等等。 
            對于proc_file_operations,我們可以看到,只實現了三個函數: 
            static struct file_operations proc_file_operations = { 
            llseek: proc_file_lseek, 
            read: proc_file_read, 
            write: proc_file_write, 
            }; 
            下面我們簡單的看一下它們實現的功能: 
            (1)llseek: proc_file_lseek 
            這個函數,用來實現lseek系統調用,其功能是設置file結構的->f_pos域,因此,根據第三個參數orig的不同,將f_pos設置為相應的值,該函數非常簡單,因此不作過多的介紹。 
            (2)read: proc_file_read 
            這個函數是file_operations結構中的成員,在后面我們將看到,在proc_dir_entry結構中實現的file_operations和inode_operations將鏈接至VFS的inode中,因此,該函數將用來實現read系統調用。在這個函數中,首先根據file結構,得到相應的inode,然后由 
            struct proc_dir_entry * dp; 
            dp = (struct proc_dir_entry *) inode->u.generic_ip; 
            而得到proc_dir_entry結構,然后,開始調用該proc_dir_entry結構中的函數,向用戶空間返回指定大小的數據,我們看一下下面的代碼片斷: 
            if (dp->get_info) { 
            /* 
            * Handle backwards compatibility with the old net 
            * routines. 
            */ 
            n = dp->get_info(page, &start, *ppos, count); 
            if (n read_proc) { 
            n = dp->read_proc(page, &start, *ppos, 
            count, &eof, dp->data); 
            } else 
            break; 
            由此我們看出,該函數的實現依賴于proc_dir_entry結構中的get_info和read_proc函數,因此,如果我們要注冊自己的proc文件,在不設置自己的proc_fops操作函數集的時候,必須實現上面兩個函數中的一個,否則,這個缺省的proc_file_read函數將做不了任何工作。示意圖如下: 
            在這個函數中,實現了從內核空間向用戶空間傳遞數據的功能,其中使用了許多技巧,在這里就不作討論了,具體實現可以參考源碼。 
            (3)write: proc_file_write 
            與上面的函數類似,我們可以看到proc_file_write函數同樣依賴于proc_dir_entry中的write_proc(file, buffer, count, dp->data)函數,它的實現非常簡單: 
            static ssize_t 
            proc_file_write(struct file * file, const char * buffer, 
            size_t count, loff_t *ppos) 
            { 
            struct inode *inode = file->f_dentry->d_inode; 
            struct proc_dir_entry * dp; 
            dp = (struct proc_dir_entry *) inode->u.generic_ip; 
            if (!dp->write_proc) 
            return -EIO; 
            /* FIXME: does this routine need ppos? probably... */ 
            return dp->write_proc(file, buffer, count, dp->data); 
            } 
            我們看到,它只是簡單地檢測了->write_proc函數是否存在,如果我們在proc_dir_entry結構中實現了這個函數,那么就調用它,否則,就退出。 
            根據上面的討論,我們看到,對于普通文件的操作函數,proc文件系統為我們提供了一個簡單的封裝,因此,我們只要在proc_dir_entry中實現相關的讀寫操作即可。 
            但是,如果我們想提供讀寫操作之外的函數,那么我們就可以定義自己的file_operations函數集,并且在proc文件注冊后,將它鏈接到proc_dir_entry的proc_fops上,這樣,就可以使用自己的函數集了。 
            2 對鏈接文件的操作 
            根據代碼段: 
            else if (S_ISLNK(dp->mode)) { 
            if (dp->proc_iops == NULL) 
            dp->proc_iops = &proc_link_inode_operations; 
            我們可以看出,對于鏈接文件,proc文件系統為它設置了索引節點操作proc_iops。因為我們知道,一個符號鏈接,只擁有inode結構,而沒有文件結構,所以,為它提供proc_link_inode_operations函數集就可以了。 
            下面我們看一下,這個函數集的內容: 
            static struct inode_operations proc_link_inode_operations = { 
            readlink: proc_readlink, 
            follow_link: proc_follow_link, 
            }; 
            這個函數集實現了和鏈接相關的兩個函數,我們分別來看一下: 
            (1)readlink: proc_readlink 
            該函數用來實現readlink系統調用,它的功能是獲得目標文件的文件名,我們在前面看到,對于一個鏈接文件,在注冊時已經將鏈接目標的文件放在了proc_dir_entry結構的->data域中(參考前面介紹的函數proc_symlink),因此,我們只要將->data中的數據返回就可以了,它的代碼如下: 
            static int proc_readlink(struct dentry *dentry, char *buffer, int buflen) 
            { 
            char *s= 
            ((struct proc_dir_entry *)dentry->d_inode->u.generic_ip)->data; 
            return vfs_readlink(dentry, buffer, buflen, s); 
            } 
            我們看到,這個函數使用一個指針指向->data,然后,使用VFS函數vfs_readlink將數據返回到用戶空間,非常的簡單。 
            (2)follow_link: proc_follow_link 
            這個函數代碼如下: 
            static int proc_follow_link(struct dentry *dentry, struct nameidata *nd) 
            { 
            char *s= 
            ((struct proc_dir_entry *)dentry->d_inode->u.generic_ip)->data; 
            return vfs_follow_link(nd, s); 
            } 
            和上面介紹的函數類似,它同樣利用VFS的函數實現其功能,對于vfs_follow_link,可以參考fs/namei.c文件。其結構如下圖所示: 
            3 對目錄文件的操作 
            最后我們看一下proc文件系統對目錄文件的操作函數集,在文件注冊的時候,有如下代碼: 
            if (S_ISDIR(dp->mode)) { 
            if (dp->proc_iops == NULL) { 
            dp->proc_fops = &proc_dir_operations; 
            dp->proc_iops = &proc_dir_inode_operations; 
            } 
            dir->nlink++; 
            } 
            從中我們可以看到,在proc文件系統中注冊目錄文件的時候,它會檢查是否該proc_dir_entry結構已經注冊了proc_iops函數集,如果沒有,那么就為proc_fops和proc_iops設置相應的缺省函數集。下面我們對它們分別進行討論: 
            1.對目錄的文件操作proc_dir_operations: 
            static struct file_operations proc_dir_operations = { 
            read: generic_read_dir, 
            readdir: proc_readdir, 
            }; 
            這個函數集的主要功能,是在由proc_dir_entry結構構成的proc文件樹中解析目錄。下面我們對這兩個函數進行一個簡單的分析: 
            (1)read: generic_read_dir 
            我們知道,對于read系統調用,當其參數文件句柄指向目錄的時候,將返回EISDIR錯誤。因此,目錄文件的read函數將完成這個工作。generic_read_dir函數是VFS提供的通用函數,可以參考fs/read_write.c文件: 
            ssize_t generic_read_dir(struct file *filp, char *buf, size_t siz, loff_t *ppos){ 
            return –EISDIR; 
            } 
            這個函數很簡單,只要返回錯誤碼就可以了。 
            (2)readdir: proc_readdir 
            這個函數用來實現readdir系統調用,它從目錄文件中讀出dirent結構到內存中。我們可以參考fs/readdir.c中的filldir()函數。 
            2.對目錄文件索引節點的操作函數:proc_dir_inode_operations 
            首先,我們看一下proc_dir_inode_operations的定義: 
            /* 
            * proc directories can do almost nothing.. 
            */ 
            static struct inode_operations proc_dir_inode_operations = { 
            lookup: proc_lookup, 
            }; 
            我們看到,對于目錄文件的索引節點,只定義了一個函數lookup。因為我們在前面對VFS進行分析的時候知道,以下操作,是只在目錄節點中定義的: 
            int (*create) (struct inode *,struct dentry *,int); 
            struct dentry * (*lookup) (struct inode *,struct dentry *); 
            int (*link) (struct dentry *,struct inode *,struct dentry *); 
            int (*unlink) (struct inode *,struct dentry *); 
            int (*symlink) (struct inode *,struct dentry *,const char *); 
            int (*mkdir) (struct inode *,struct dentry *,int); 
            int (*rmdir) (struct inode *,struct dentry *); 
            int (*mknod) (struct inode *,struct dentry *,int,int); 
            int (*rename) (struct inode *, struct dentry *, 
            struct inode *, struct dentry *); 
            但是經過我們對proc文件系統的分析,我們知道,proc文件系統中的文件都是在內核代碼中通過proc_dir_entry實現的,因此,它不提供目錄索引節點的create,link,unlink,symlink,mkdir,rmdir,mknod,rename方法,也就是說,用戶是不能通過shell命令在/proc目錄中對proc文件進行改名,刪除,建子目錄等操作的。這也算是proc文件系統的一種保護策略。 
            而在內核中,則使用proc_mkdir,proc_mknod等函數,在核心內通過代碼來維護proc文件樹。由此可以看出虛擬文件系統的一些特性。對目錄文件的默認操作,可以參見下面的示意圖: 
            下面我們就來看一下唯一定義的函數lookup: proc_lookup,到底實現了什么功能。 
            在進行具體分析之前,我們先考慮一個問題,我們知道,proc文件系統維護了自己的proc_dir_entry結構,因此提供了create_proc_entry,remove_proc_entry等等函數,并且為了方便實現對proc文件的讀寫功能,特意在proc_dir_entry結構中設置了get_info,read_proc和write_proc函數指針(我們在前面介紹過,這三個函數被封裝在proc_file_operations中),并且,提供了自己的inode_operations和file_operations,分別是proc_iops 和proc_fops。也就是說,我們在建立proc文件以及為proc文件建立操作函數的時候,似乎可以不用考慮VFS的實現,只要建立并注冊該proc_dir_entry結構,然后實現其proc_iops 和proc_fops(或者get_info,read_proc和write_proc)就可以了。 
            但是我們知道,在linux系統中,所有的子系統都是與VFS層交互,而VFS是通過inode結構進行管理的,并且在其上的操作(文件和索引節點的操作)也是通過該inode結構的inode_operations和file_operations實現的。因此,proc文件系統必須將自己的文件與VFS的inode鏈接起來。 
            那么proc文件系統是在何時,通過何種方法將自己的proc_dir_entry結構和VFS的inode聯系在一起的,并且將對inode的inode_operations和file_operations操作定位到自己結構中的proc_iops 和proc_fops上呢?通過我們對lookup: proc_lookup的分析,就會明白這一過程。 
            我們先看一下它的代碼: 
            struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry) 
            { 
            struct inode *inode; 
            struct proc_dir_entry * de; 
            int error; 
            error = -ENOENT; 
            inode = NULL; 
            de = (struct proc_dir_entry *) dir->u.generic_ip; 
            if (de) { 
            for (de = de->subdir; de ; de = de->next) { 
            if (!de || !de->low_ino) 
            continue; 
            if (de->namelen != dentry->d_name.len) 
            continue; 
            if (!memcmp(dentry->d_name.name, 
            de->name, de->namelen)) { 
            int ino = de->low_ino; 
            error = -EINVAL; 
            inode = proc_get_inode(dir->i_sb, ino, de); 
            break; 
            } 
            } 
            } 
            if (inode) { 
            dentry->d_op = &proc_dentry_operations; 
            d_add(dentry, inode); 
            return NULL; 
            } 
            return ERR_PTR(error); 
            } 
            這個函數的參數是struct inode * dir和struct dentry *dentry,它的功能是查找由dentry指定的文件,是否在由dir指定的目錄中。 
            我們知道,proc文件系統通過proc_dir_entry結構維護文件信息,并且該結構與相應的inode->u.generic_ip聯系,因此,這個函數首先通過struct inode * dir得到了相應目錄文件的proc_dir_entry結構,并使用指針de指向它,然后,開始在該結構的孩子中查找指定的dentry。 
            判斷是否找到的條件很簡單,就是de->namelen等于 dentry->d_name.len,并且dentry->d_name.name等于de->name,根據程序流程,如果沒有找到,那么將返回-ENOENT錯誤(使用inode指針作為判斷條件),如果找到該文件,那么就根據ino = de->low_ino(要注意的是,這時候的de已經指向由dentry確定的proc_dir_entry結構了。)調用函數: 
            inode = proc_get_inode(dir->i_sb, ino, de); 
            這個proc_get_inode的功能很容易猜到,就是從由超級塊i_sb確定的文件系統中,得到索引節點號為ino的inode。因此考慮兩種情況,第一種情況,這個索引節點已經被讀入緩存了,那么直接返回該inode即可。第二種情況是,指定ino的索引節點不在緩存中,那么就需要調用相應的函數,將該索引節點從邏輯文件系統中讀入inode中。 
            下面我們就來分析一下proc_get_inode函數,尤其注意上面所說的第二種情況,因為這正是inode和proc_dir_entry建立聯系并重定位操作函數集的時機。先看一下源碼: 
            struct inode * proc_get_inode(struct super_block * sb, int ino, 
            struct proc_dir_entry * de) 
            { 
            struct inode * inode; 
            /* 
            * Increment the use count so the dir entry can't disappear. 
            */ 
            de_get(de); 
            #if 1 
            /* shouldn't ever happen */ 
            if (de && de->deleted) 
            printk("proc_iget: using deleted entry %s, count=%d\n", de->name, atomic_read(&de->count)); 
            #endif 
            inode = iget(sb, ino); 
            if (!inode) 
            goto out_fail; 
            inode->u.generic_ip = (void *) de; /* link the proc_dir_entry to inode */ 
            /* 
            * set up other fields in the inode 
            */ 
            if (de) { 
            if (de->mode) { 
            inode->i_mode = de->mode; 
            inode->i_uid = de->uid; 
            inode->i_gid = de->gid; 
            } 
            if (de->size) 
            inode->i_size = de->size; 
            if (de->nlink) 
            inode->i_nlink = de->nlink; 
            if (de->owner) 
            __MOD_INC_USE_COUNT(de->owner); 
            if (S_ISBLK(de->mode)||S_ISCHR(de->mode)||S_ISFIFO(de->mode)) 
            init_special_inode(inode,de->mode,kdev_t_to_nr(de->rdev)); 
            else { 
            if (de->proc_iops) 
            inode->i_op = de->proc_iops; 
            if (de->proc_fops) 
            inode->i_fop = de->proc_fops; 
            } 
            } 
            out: 
            return inode; 
            out_fail: 
            de_put(de); 
            goto out; 
            } 
            我們根據程序流程,分析它的功能: 
            1.使用de_get(de)增加proc_dir_entry結構de的引用計數。 
            2.使用VFS的iget(sb, ino)函數,從sb指定的文件系統中得到節點號為ino的索引節點,并使用指針inode指向它。如果沒有得到,則直接跳到標號out_fail,減少de的引用計數后退出。 
            因此我們要了解一下iget,這個函數由VFS提供,可以參考源文件fs/inode.c和頭文件include/linux/fs.h,在fs.h頭文件中,有如下定義: 
            static inline struct inode *iget(struct super_block *sb, unsigned long ino) 
            { 
            return iget4(sb, ino, NULL, NULL); 
            } 
            因此該函數是由fs/inode.c中的iget4實現的。主要步驟是,首先根據sb和ino得到要查找的索引節點的哈希鏈表,然后調用find_inode函數在該鏈表中查找該索引節點。如果找到了,那么就增加該索引節點的引用計數,并將其返回;否則,調用get_new_inode函數,以便從邏輯文件系統中讀出該索引節點。 
            而get_new_inode函數也很簡單,它分配一個inode結構,并試圖重新查找指定的索引節點,如果還是沒有找到,那么就給新分配的索引節點加入到哈希鏈表和使用鏈表中,并設置一些基本信息,如i_ino,i_sb,i_dev等,并且,將其引用計數i_count初始化為1。然后,調用超級塊sb的read_inode函數,來作邏輯文件系統自己特定的工作,但對于proc文件系統來說,read_inode函數基本沒有實質性的功能,可參考前文對該函數的分析。最后,返回這個新建的索引節點。 
            3.這時,我們已經得到了指定的inode(或者是從緩存中返回,或者是利用get_new_inode函數剛剛創建),那么就使用語句 
            inode->u.generic_ip = (void *) de; 
            將proc_dir_entry結構de與相應的索引節點鏈接起來。因此,我們就可以在其他時刻,利用proc文件索引節點的->u.generic_ip得到相應的proc_dir_entry結構了。 
            對于新創建的inode來說,將其->u.generic_ip域指向(void *) de沒什么問題,因為該域還沒有被賦值,但是如果這個inode是從緩存中得到的,那么,說明該域已經指向了一個proc_dir_entry結構,這樣直接賦值,會不會引起問題呢? 
            這有兩種情況,第一種情況,它指向的proc_dir_entry結構沒有發生過變化,那么,由于索引節點是由ino確定的,而且在一個文件系統中,確保了索引節點號ino的唯一性,因此,使用inode->u.generic_ip = (void *) de語句對其重新進行賦值,不會發生任何問題。 
            另一種情況是在這之前,程序曾調用remove_proc_entry要將該proc_dir_entry結構刪除,那么由于它的引用計數count不等于零,因此,該結構不會被釋放,而只是打上了刪除標記。所以這種情況下,該賦值語句也不會引起問題。 
            我們知道,當inode的i_count變為0的時候,會調用sb的proc_delete_inode函數,這個函數將inode的i_state設置為I_CLEAR,這可以理解為將該inode刪除了,并調用de_put,減少并檢查proc_dir_entry的引用計數,如果到零,也將其釋放。因此我們看到,引用計數的機制使得VFS的inode結構和proc的proc_dir_entry結構能夠保持同步,也就是說,對于一個存在于緩存中的的inode,必有一個proc_dir_entry結構存在。 
            4.這時,我們已經得到了inode結構,并且將相應的proc_dir_entry結構de與inode鏈接在了一起。因此,就可以根據de的信息,對inode的一些域進行填充了。其中最重要的是使用語句: 
            if (de->proc_iops) 
            inode->i_op = de->proc_iops; 
            if (de->proc_fops) 
            inode->i_fop = de->proc_fops; 
            將inode的操作函數集重定向到proc_dir_entry結構提供的函數集上。這是因為我們可以通過proc_dir_entry結構進行方便的設置和調整,但最終要將文件提交至VFS進行管理。正是在這種思想下,proc文件系統提供提供了一套封裝函數,使得我們可以只對proc_dir_entry結構進行操作,而忽略與VFS的inode的聯系。 
            5.最后,成功地返回所要的inode結構。 
            (七) 小結 
            至此,已經對proc文件系統進行了一個粗略的分析,從文件系統的注冊,到proc_dir_entry結構的管理,以及與VFS的聯系等等。下面我們對proc文件系統的整體結構作一個總結。 
            proc文件系統使用VFS接口,注冊自己的文件類型,并且通過注冊時提供的proc_read_super函數,創建自己的超級塊,然后裝載vfsmount結構。在proc文件系統內部,則使用proc_dir_entry結構來維護自己的文件樹,并且通過目錄文件的lookup函數,將proc_dir_entry結構與VFS的inode結構建立聯系。 


            本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u2/74524/showart_1129842.html

            posted on 2010-06-23 06:10 小默 閱讀(574) 評論(0)  編輯 收藏 引用 所屬分類: Linux

            導航

            統計

            留言簿(13)

            隨筆分類(287)

            隨筆檔案(289)

            漏洞

            搜索

            積分與排名

            最新評論

            閱讀排行榜

            久久精品国产99久久久| 无码人妻久久一区二区三区免费丨| 99久久夜色精品国产网站| 人妻无码αv中文字幕久久琪琪布| 久久久久久久综合日本| 久久精品免费一区二区| jizzjizz国产精品久久| 久久久久综合中文字幕 | 国产成人久久AV免费| 久久成人精品视频| 麻豆久久久9性大片| 久久A级毛片免费观看| 国产精品成人精品久久久| 精品久久久久久国产| 久久久99精品一区二区| 97久久精品国产精品青草| 久久久久亚洲AV无码专区桃色| 色妞色综合久久夜夜| 一本大道久久香蕉成人网| 国产精品久久久久影视不卡| 久久久午夜精品福利内容| 51久久夜色精品国产| 久久综合给久久狠狠97色| 亚洲日本久久久午夜精品| 久久免费精品视频| 1000部精品久久久久久久久| 欧美激情一区二区久久久| 久久夜色撩人精品国产小说| 青青国产成人久久91网| 国产一区二区精品久久| 天堂久久天堂AV色综合| 久久人人爽人人爽人人片AV高清| 欧美日韩精品久久久久 | 无码人妻久久久一区二区三区| 久久无码一区二区三区少妇 | 色综合久久88色综合天天 | 成人亚洲欧美久久久久| Xx性欧美肥妇精品久久久久久| 久久久久国产精品| 久久夜色精品国产亚洲| 久久九九有精品国产23百花影院|