作者:
五) 對(duì)proc文件的管理
前面我們提過,相對(duì)于其他邏輯文件系統(tǒng)的具體文件組織形式(比如ext2文件系統(tǒng)的 inode),proc文件系統(tǒng)也有自己的組織結(jié)構(gòu),那就是proc_dir_entry結(jié)構(gòu),所有屬于proc文件系統(tǒng)的文件,都對(duì)應(yīng)一個(gè) proc_dir_entry結(jié)構(gòu),并且在VFS需要讀取proc文件的時(shí)候,把這個(gè)結(jié)構(gòu)和VFS的inode建立鏈接(即由 inode->u.generic_ip指向該prc_dir_entry結(jié)構(gòu))。 因此,proc文件系統(tǒng)實(shí)現(xiàn)了一套對(duì)proc_dir_entry結(jié)構(gòu)的管理,下面我們就此進(jìn)行一個(gè)分析。 1 proc_dir_entry結(jié)構(gòu) 首先我們看一下proc_dir_entry結(jié)構(gòu),這個(gè)結(jié)構(gòu)在proc_fs.h中定義: struct proc_dir_entry { unsigned short low_ino; unsigned short namelen; const char *name; mode_t mode; nlink_t nlink; uid_t uid; gid_t gid; unsigned long size; struct inode_operations * proc_iops; struct file_operations * proc_fops; get_info_t *get_info; struct module *owner; struct proc_dir_entry *next, *parent, *subdir; void *data; read_proc_t *read_proc; write_proc_t *write_proc; atomic_t count; /* use count */ int deleted; /* delete flag */ kdev_t rdev; }; 在這個(gè)結(jié)構(gòu)中,描述了一個(gè)proc文件的全部信息,每一個(gè)proc文件正是使用proc_dir_entry結(jié)構(gòu)來表示的。下面我們看一下它最重要的幾個(gè)域: low_ino:這是用來唯一標(biāo)志proc_dir_entry結(jié)構(gòu)的節(jié)點(diǎn)號(hào),也就是proc文件系統(tǒng)內(nèi)的索引節(jié)點(diǎn)的標(biāo)號(hào),除了根結(jié)點(diǎn),其他的節(jié)點(diǎn)號(hào)都是在創(chuàng)建proc_dir_entry的時(shí)候,由make_inode_number()動(dòng)態(tài)創(chuàng)建的。 name:即這個(gè)proc文件的名字。 mode: 該proc文件的模式由兩部分用位或運(yùn)算組成,第一部分是文件的類型,可以參考include/linux/stat.h中的定義,比如,S_IFREG 表示普通文件,而S_IFDIR表示目錄文件。第二部分是該文件的權(quán)限,同樣可以參考include/linux/stat.h中的定義,比 如,S_IRUSR表示該文件能夠被擁有者讀,S_IROTH 表示該文件可以被其他人讀取。但真正的權(quán)限檢查,我們可以放到后面提到的inode_operations結(jié)構(gòu)中。 size:即我們使用“l(fā)s”命令時(shí),所顯示出的文件大小。 proc_iops: 這是一個(gè)inode_operations結(jié)構(gòu),其中設(shè)置了針對(duì)這個(gè)proc索引節(jié)點(diǎn)的操作函數(shù),這樣,我們就可以針對(duì)不同類型的proc文件,提供不同 的方法,以完成不同的工作。比如我們上面提到的對(duì)proc文件的權(quán)限檢查,就可以放在這個(gè)結(jié)構(gòu)中。 proc_fops:這是一個(gè)file_operations結(jié)構(gòu),其中放置了針對(duì)這個(gè)proc文件的操作函數(shù),我們可以把對(duì)proc文件的讀寫操作,放在這個(gè)結(jié)構(gòu)中,用以實(shí)現(xiàn)對(duì)/proc目錄中的文件的讀,寫功能。 get_info:當(dāng)用戶向proc文件讀取的數(shù)據(jù)小于一個(gè)頁面大小時(shí),可以使用這個(gè)函數(shù)向用戶返回?cái)?shù)據(jù)。 struct proc_dir_entry *next, *parent, *subdir:使用這些鏈表,在內(nèi)存中,proc_dir_entry結(jié)構(gòu)就以樹的形式鏈接在一起。 read_proc_t *read_proc 和write_proc_t *write_proc:這兩個(gè)函數(shù)提供了對(duì)proc文件進(jìn)行操作的簡單接口。我們知道,對(duì)于proc文件,我們可以從中讀取核心數(shù)據(jù),還可以向其中寫入 數(shù)據(jù),因此,對(duì)于一些功能比較簡單的proc文件,我們只要實(shí)現(xiàn)這兩個(gè)函數(shù)(或其中之一)即可,而不用設(shè)置inode_operations結(jié)構(gòu),這樣, 整個(gè)操作比較簡單。實(shí)際上,我們會(huì)在后面的分析中看到,在注冊(cè)proc文件的時(shí)候,會(huì)自動(dòng)為proc_fops設(shè)置一個(gè)缺省的 file_operations結(jié)構(gòu),如果我們只實(shí)現(xiàn)了上面提到的兩個(gè)讀寫操作,而沒有設(shè)置自己file_operations結(jié)構(gòu),那么,會(huì)由缺省的 inode_operations結(jié)構(gòu)中的讀寫函數(shù)檢查調(diào)用這兩個(gè)函數(shù)。 atomic_t count:該結(jié)構(gòu)的使用計(jì)數(shù)。當(dāng)一個(gè)proc_dir_entry結(jié)構(gòu)的count減為零時(shí),會(huì)釋放該結(jié)構(gòu),這種結(jié)果就像把一個(gè)ext2文件系統(tǒng)的文件從磁盤上刪除掉一樣。 int deleted:這是一個(gè)刪除標(biāo)志,當(dāng)我們調(diào)用remove_proc_entry函數(shù)要?jiǎng)h除一個(gè)proc_dir_entry時(shí),如果發(fā)現(xiàn)該結(jié)構(gòu)還在使用,就會(huì)設(shè)置該標(biāo)志并且推出。 2 建立proc文件 在了解了proc_dir_entry結(jié)構(gòu)之后,我們來看一看proc文件系統(tǒng)是如何管理自己的文件結(jié)構(gòu)的。 首先我們看一看它是如何創(chuàng)建proc文件的,參考文件fs/proc/generic.c,其中,有一個(gè)函數(shù)create_proc_entry,由它創(chuàng)建并注冊(cè)proc文件,下面我們看一下它的源碼: struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent) { struct proc_dir_entry *ent = NULL; const char *fn = name; int len; if (!parent && xlate_proc_name(name, &parent, &fn) != 0) goto out; len = strlen(fn); ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL); if (!ent) goto out; memset(ent, 0, sizeof(struct proc_dir_entry)); memcpy(((char *) ent) + sizeof(*ent), fn, len + 1); ent->name = ((char *) ent) + sizeof(*ent); ent->namelen = len; if (S_ISDIR(mode)) { if ((mode & S_IALLUGO) == 0) mode |= S_IRUGO | S_IXUGO; ent->proc_fops = &proc_dir_operations; ent->proc_iops = &proc_dir_inode_operations; ent->nlink = 2; } else { if ((mode & S_IFMT) == 0) mode |= S_IFREG; if ((mode & S_IALLUGO) == 0) mode |= S_IRUGO; ent->nlink = 1; } ent->mode = mode; proc_register(parent, ent); /* link ent to parent */ out: return ent; } 我 們看到,首先,該函數(shù)會(huì)做一些必要的檢查,比如要確保它的父節(jié)點(diǎn)必須存在等等。其次會(huì)創(chuàng)建一個(gè)proc_dir_entry結(jié)構(gòu),并且為該文件的名字也分 配空間,并用->name指向它。再次,會(huì)根據(jù)該文件的類型,設(shè)置適當(dāng)?shù)哪J胶玩溄訑?shù)。最后,會(huì)調(diào)用proc_register(parent, ent)函數(shù),將這個(gè)結(jié)構(gòu)鏈接到proc文件樹中。 下面我們看一下它的實(shí)現(xiàn)代碼: static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) { int i; i = make_inode_number(); if (i < 0) return -EAGAIN; dp->low_ino = i; dp->next = dir->subdir; dp->parent = dir; dir->subdir = dp; 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; } return 0; } 這 個(gè)函數(shù)主要完成三部分的工作,第一,使用make_inode_number()函數(shù)動(dòng)態(tài)的到一個(gè)節(jié)點(diǎn)號(hào),并且設(shè)置low_ino。第二步,將這個(gè) proc_dir_entry結(jié)構(gòu)鏈接到它的父節(jié)點(diǎn)上。第三步,根據(jù)文件類型的不同,設(shè)置不同的(索引節(jié)點(diǎn)和文件)缺省操作函數(shù)集。 這樣,一個(gè)proc文件就注冊(cè)成功了。 3 刪除proc文件 在同一源文件中,提供了刪除proc_dir_entry結(jié)構(gòu)的函數(shù),即remove_proc_entry,下面我們分析一下它的實(shí)現(xiàn)過程。 void remove_proc_entry(const char *name, struct proc_dir_entry *parent) { struct proc_dir_entry **p; struct proc_dir_entry *de; const char *fn = name; int len; if (!parent && xlate_proc_name(name, &parent, &fn) != 0) goto out; len = strlen(fn); for (p = &parent->subdir; *p; p=&(*p)->next ) { if (!proc_match(len, fn, *p)) continue; de = *p; *p = de->next; de->next = NULL; if (S_ISDIR(de->mode)) parent->nlink--; clear_bit(de->low_ino-PROC_DYNAMIC_FIRST, (void *) proc_alloc_map); proc_kill_inodes(de); de->nlink = 0; if (!atomic_read(&de->count)) free_proc_entry(de); else { de->deleted = 1; printk("remove_proc_entry: %s/%s busy, count=%d\n", parent->name, de->name, atomic_read(&de->count)); } break; } out: return; } 該函數(shù)在參數(shù)parent的所有孩子中查找指定的名字,如果找到匹配的節(jié)點(diǎn),即proc_match(len, fn, *p),那么,就將該結(jié)構(gòu)從樹結(jié)構(gòu)中去掉。然后,如果刪除的proc_dir_entry是目錄結(jié)構(gòu),那么,就減少其父節(jié)點(diǎn)的鏈接數(shù)。 然后,調(diào)用clear_bit(de->low_ino-PROC_DYNAMIC_FIRST, (void *) proc_alloc_map)函數(shù),清除該節(jié)點(diǎn)號(hào)。 最后,將該結(jié)構(gòu)的鏈接數(shù)置零,并調(diào)用atomic_read(&de->count)來檢查它的引用計(jì)數(shù),如果是零,那么就使用函數(shù)free_proc_entry釋放該節(jié)點(diǎn),否則,就將它的刪除標(biāo)記位置一,在以后適當(dāng)?shù)貦C(jī)會(huì)中,再將其釋放。 4 其他管理函數(shù) 除此之外,我們看到還有一些函數(shù),可以方便我們管理和使用proc文件系統(tǒng),我們簡單地介紹一下: struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent)函數(shù),這個(gè)函數(shù)用來在proc文件系統(tǒng)中注冊(cè)一個(gè)子目錄,根據(jù)它的參數(shù),我們就可以看出它的功能。在這個(gè)函數(shù)里,將動(dòng)態(tài)分配一個(gè) proc_dir_entry結(jié)構(gòu)以及它的名字,然后,設(shè)置目錄文件的缺省操作(proc_iops以及proc_fops)以及nlink值,最后,調(diào) 用proc_register函數(shù)將其注冊(cè)。 struct proc_dir_entry *proc_mknod(const char *name, mode_t mode, struct proc_dir_entry *parent, kdev_t rdev)函數(shù),用來在proc文件系統(tǒng)中建立一個(gè)設(shè)備文件,因此,在創(chuàng)建proc_dir_entry結(jié)構(gòu)后,沒有設(shè)置缺省操作,而是使用 ->rdev = rdev指定了設(shè)備。最后,調(diào)用proc_register函數(shù)將其注冊(cè)。 struct proc_dir_entry *proc_symlink(const char *name, struct proc_dir_entry *parent, const char *dest)函數(shù),該函數(shù)創(chuàng)建了一個(gè)鏈接文件,使用->mode = S_IFLNK|S_IRUGO|S_IWUGO|S_IXUGO來標(biāo)志,它和其他文件的建立很相似,只是,它將鏈接的目標(biāo)文件名放在了 ->data域中。最后,它同樣調(diào)用proc_register函數(shù)將該結(jié)構(gòu)注冊(cè)。 |
|