原文地址:
作者:
(journeyman)不知正确与否。
这样是表示对作者的尊重。
如果有不对,请通知我,我将即时更改. | |
五) 对proc文件的管理
前面我们提过,相对于其他逻辑文件系统的具体文件组织形式(比如ext2文件系统的inode),proc文件系统也有自己的组织结构,那就是proc_dir_entry结构,所有属于proc文件系统的文件,都对应一个proc_dir_entry结构,并且在VFS需要读取proc文件的时候,把这个结构和VFS的inode建立链接(即由inode->u.generic_ip指向该prc_dir_entry结构)。
因此,proc文件系统实现了一套对proc_dir_entry结构的管理,下面我们就此进行一个分析。
1 proc_dir_entry结构
首先我们看一下proc_dir_entry结构,这个结构在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;
};
在这个结构中,描述了一个proc文件的全部信息,每一个proc文件正是使用proc_dir_entry结构来表示的。下面我们看一下它最重要的几个域:
low_ino:这是用来唯一标志proc_dir_entry结构的节点号,也就是proc文件系统内的索引节点的标号,除了根结点,其他的节点号都是在创建proc_dir_entry的时候,由make_inode_number()动态创建的。
name:即这个proc文件的名字。
mode:该proc文件的模式由两部分用位或运算组成,第一部分是文件的类型,可以参考include/linux/stat.h中的定义,比如,S_IFREG表示普通文件,而S_IFDIR表示目录文件。第二部分是该文件的权限,同样可以参考include/linux/stat.h中的定义,比如,S_IRUSR表示该文件能够被拥有者读,S_IROTH 表示该文件可以被其他人读取。但真正的权限检查,我们可以放到后面提到的inode_operations结构中。
size:即我们使用“ls”命令时,所显示出的文件大小。
proc_iops:这是一个inode_operations结构,其中设置了针对这个proc索引节点的操作函数,这样,我们就可以针对不同类型的proc文件,提供不同的方法,以完成不同的工作。比如我们上面提到的对proc文件的权限检查,就可以放在这个结构中。
proc_fops:这是一个file_operations结构,其中放置了针对这个proc文件的操作函数,我们可以把对proc文件的读写操作,放在这个结构中,用以实现对/proc目录中的文件的读,写功能。
get_info:当用户向proc文件读取的数据小于一个页面大小时,可以使用这个函数向用户返回数据。
struct proc_dir_entry *next, *parent, *subdir:使用这些链表,在内存中,proc_dir_entry结构就以树的形式链接在一起。
read_proc_t *read_proc 和write_proc_t *write_proc:这两个函数提供了对proc文件进行操作的简单接口。我们知道,对于proc文件,我们可以从中读取核心数据,还可以向其中写入数据,因此,对于一些功能比较简单的proc文件,我们只要实现这两个函数(或其中之一)即可,而不用设置inode_operations结构,这样,整个操作比较简单。实际上,我们会在后面的分析中看到,在注册proc文件的时候,会自动为proc_fops设置一个缺省的file_operations结构,如果我们只实现了上面提到的两个读写操作,而没有设置自己file_operations结构,那么,会由缺省的inode_operations结构中的读写函数检查调用这两个函数。
atomic_t count:该结构的使用计数。当一个proc_dir_entry结构的count减为零时,会释放该结构,这种结果就像把一个ext2文件系统的文件从磁盘上删除掉一样。
int deleted:这是一个删除标志,当我们调用remove_proc_entry函数要删除一个proc_dir_entry时,如果发现该结构还在使用,就会设置该标志并且推出。
2 建立proc文件
在了解了proc_dir_entry结构之后,我们来看一看proc文件系统是如何管理自己的文件结构的。
首先我们看一看它是如何创建proc文件的,参考文件fs/proc/generic.c,其中,有一个函数create_proc_entry,由它创建并注册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;
}
我们看到,首先,该函数会做一些必要的检查,比如要确保它的父节点必须存在等等。其次会创建一个proc_dir_entry结构,并且为该文件的名字也分配空间,并用->name指向它。再次,会根据该文件的类型,设置适当的模式和链接数。最后,会调用proc_register(parent, ent)函数,将这个结构链接到proc文件树中。
下面我们看一下它的实现代码:
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;
}
这个函数主要完成三部分的工作,第一,使用make_inode_number()函数动态的到一个节点号,并且设置low_ino。第二步,将这个proc_dir_entry结构链接到它的父节点上。第三步,根据文件类型的不同,设置不同的(索引节点和文件)缺省操作函数集。
这样,一个proc文件就注册成功了。
3 删除proc文件
在同一源文件中,提供了删除proc_dir_entry结构的函数,即remove_proc_entry,下面我们分析一下它的实现过程。
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;
}
该函数在参数parent的所有孩子中查找指定的名字,如果找到匹配的节点,即proc_match(len, fn, *p),那么,就将该结构从树结构中去掉。然后,如果删除的proc_dir_entry是目录结构,那么,就减少其父节点的链接数。
然后,调用clear_bit(de->low_ino-PROC_DYNAMIC_FIRST, (void *) proc_alloc_map)函数,清除该节点号。
最后,将该结构的链接数置零,并调用atomic_read(&de->count)来检查它的引用计数,如果是零,那么就使用函数free_proc_entry释放该节点,否则,就将它的删除标记位置一,在以后适当地机会中,再将其释放。
4 其他管理函数
除此之外,我们看到还有一些函数,可以方便我们管理和使用proc文件系统,我们简单地介绍一下:
struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent)函数,这个函数用来在proc文件系统中注册一个子目录,根据它的参数,我们就可以看出它的功能。在这个函数里,将动态分配一个proc_dir_entry结构以及它的名字,然后,设置目录文件的缺省操作(proc_iops以及proc_fops)以及nlink值,最后,调用proc_register函数将其注册。
struct proc_dir_entry *proc_mknod(const char *name, mode_t mode, struct proc_dir_entry *parent, kdev_t rdev)函数,用来在proc文件系统中建立一个设备文件,因此,在创建proc_dir_entry结构后,没有设置缺省操作,而是使用->rdev = rdev指定了设备。最后,调用proc_register函数将其注册。
struct proc_dir_entry *proc_symlink(const char *name, struct proc_dir_entry *parent, const char *dest)函数,该函数创建了一个链接文件,使用->mode = S_IFLNK|S_IRUGO|S_IWUGO|S_IXUGO来标志,它和其他文件的建立很相似,只是,它将链接的目标文件名放在了->data域中。最后,它同样调用proc_register函数将该结构注册。