proc文件系统分析.doc_第1页
proc文件系统分析.doc_第2页
proc文件系统分析.doc_第3页
proc文件系统分析.doc_第4页
proc文件系统分析.doc_第5页
已阅读5页,还剩12页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

proc文件系统分析 根据前面的分析,我们能基本确定对proc文件系统的分析步骤。我将按照proc文件系统注册,安装的顺序对其进行分析,然后基于代码,对proc文件系统的结构进行分析,尤其是proc文件系统用于内部管理的数据结构。最后,我们将根据分析结果,提出可行的xml封装计划。 在对proc文件系统的数据结构的分析中,我将把重点放在数据输出的分析上,他是提出一种标准的XML封装方法的基础。 (一) Linux 相关原始码简介 在linux代码树中,所有文件系统的代码都放在linux/fs/目录中,其中,proc文件系统的原始码在linux/fs/proc中,下面我简单介绍一下proc目录中的源文件。 在目录中共有11个相关文件,他们是: 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文件系统的管理相关,包括proc文件系统的注册,及向内核其他子系统提供的例程等等,这是最重要的一部分代码,我们将从这里开始对proc文件系统进行分析。 源文件root.c和proc文件系统的根结点的管理相关。 而base.c,array.c则用来处理/proc目录中进程的信息,包括命令行,进程状态,内存状态等等和进程相关的内容。proc_tty.c用来处理/proc/tty信息,proc_misc.c则用来管理和/proc目录中的大多数文件。 除此之外,更有两个非常重要的头文件proc_fs.h,proc_fs_i.h,我们能在/linux/include/linux/目录中找到。 (二) proc文件系统的注册 proc文件系统遵循VFS的规范,因此在使用之前,必须进行注册。我们知道,每一个文件系统,都会在自己的初始化例程中填写一个 file_system_type 的数据结构,然后调用注册函数register_filesystem(struct file_system_type *fs) 进行注册。 proc文件系统中和之相关的文件是procfs_syms.c,在该文件中,声明了proc文件系统的类型: static DECLARE_FSTYPE(proc_fs_type, proc, proc_read_super, FS_SINGLE); 而我们在 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, 因此我们能看到,我们声明了一个文件类型proc_fs_type,他的名字是“proc”,读取终极块的函数是proc_read_super,fs_flags设置为FS_SINGLE,根据源码中的说明,我们知道,当文件系统的fs_flags声明为FS_SINGLE时,说明文件系统只有一个终极块,并且,必须在注册函数之后调用kern_mount(),使得在内核范围内的vfsmnt被放置在-kern_mnt处。 下面就是proc文件系统的注册,函数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文件系统的注册非常简单,主要有如下几个步骤: 1调用register_filesystem(&proc_fs_type),用一个非常巧妙的方法将proc文件类型加入到文件类型的单向链表中,如果发生错误,则返回。 2调用kern_mount函数,该函数基本完成三个步骤,首先调用read_super()函数,在这个函数里,VFS将为proc文件系统分配一个终极块结构,并设置s_dev,s_flags等域,然后,将调用proc文件系统的自己的read_super例程,对应proc文件系统,该例程是proc_read_super(),该例程将设置终极块结构的其他值。我们将在下一节进行分析。 其次,使用add_vfsmnt()函数建立proc文件系统的vfsmount结构,并将其加入到已装载文件系统的链表中(可参考图xx)。 最后,返回该vfsmount结构,并利用返回值,使用指针proc_mnt指向该vfsmount结构。 3判断返回值是否错误,如果错误,那么就卸载文件系统。 这样,一个文件系统就成功注册到核心了。同样,proc文件系统的卸载,也非常简单,代码如下: static void _exit exit_proc_fs(void) unregister_filesystem(&proc_fs_type); kern_umount(proc_mnt); (三) 建立proc文件系统的终极块 我们刚才看到,在kern_mount函数中,调用read_proc建立了终极块结构,然后就会调用文件系统自己提供的读取终极块的例程,用来填充自己的终极块结构,下面我们看一下proc文件系统的终极块读取例程proc_read_super()是怎么工作的,及他最终完成了哪些工作,该函数在fs/proc/inode.c中实现: 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 inodes 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 failedn); iput(root_inode); return NULL; 该函数进行了如下几步操作: 1在该函数里,首先向作为参数传入的终极块写入文件系统的基本信息,s_blocksize设置为1024,由于1024210,因此,s_blocksize_bit设置为10,然后是proc文件系统的魔数,为PROC_SUPER_MAGIC。终极块的函数集设置为proc_sops,对于proc文件系统来讲,只实现了4个终极块函数,我们将在后面进行分析。然后,设置proc文件系统中的文件最大字节数为MAX_NON_LFS,在fs.h中,定义这个宏为 (1ULs_root = d_alloc_root(root_inode) 其中root_inode 的类型是struct inode *, 而s_root的类型是struct dentry *。我们在介绍VFS的时候知道,目录高速缓存以树状结构存在,因此,在建立文件系统的根结点后,需要使用d_alloc_root()函数建立一个根目录(root dentry),也就是说,该dentry结构的。 最终成功返回终极块,这时,终极块已填上了必要的数据信息。因此能看到,终极块读取例程主要完成了两部分的工作,首先向终极块写入必要的数据,其次建立了该文件系统的根结点,并在目录高速缓存中建立了相应的dentry结构。 (四) proc文件系统终极块的操作函数集 在上一节我们看到了proc文件系统怎么设置自己的终极块,并且将终极块操作函数集设置为proc_sops,这一节我们就分析一下,对于proc文件系统的终极块,需要提供什么操作,及怎么实现这些操作。 在文件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文件系统仅仅实现了4个终极块操作函数。他使用了一种比较特别的方法来初始化结构,这种方法叫作labeled elements,这是GNU的C扩展,这样在初始化结构时,不必按照结构的顺序,只要指明域名,就可初始化其值,而对于没有提到的域,将自动设置为0。 所以我们看到,proc文件系统仅仅定义了4个终极块操作函数,我们看一下为什么其他的操作函数不必定义。 首先,我们知道,proc文件系统仅仅存在于内存中,并不必物理设备,因此write_inode函数就不必定义了。而函数notify_change,在索引节点的属性被改动的时候会被调用,而对于proc文件系统的inode来说,并未提供setattr 函数,换句话说,文件的属性不会被改动,所以,notif_change也就不会被调用(proc文件系统对于inode_operations,同样仅仅提供了非常少的几种操作,并且,在建立文件树的时候,还针对不同的文件/目录,设置了不同的索引节点操作函数,这将在以后进行周详的介绍)。基于类似的原因,其他的函数,诸如put_super,write_super,及clear_inode等等函数,都没有进行定义。 下面我们看一下定义的这4个函数: 1 read_inode: proc_read_inode 这个函数用来从已装载文件系统中,读取指定索引节点的信息。实际上,在需要读取特定的索引节点时,会调用VFS的iget(sb, ino)函数,其中,sb指定了文件系统的终极块,而ino是索引节点的标号。这个函数会在该终极块的dcache中寻找该索引节点,如果找到,则返回索引节点,否则,就必须从逻辑文件系统中读取指定的索引节点,这时,会调用get_new_inode()函数,在这个函数里,会分配一个inode结构,填写一些基本的信息,然后,就会调用终极块的操作函数read_inode,对于proc文件系统而言,就是proc_read_inode()函数。 在后面的介绍里我们会知道,proc文件系统为了方便自己对文件的管理,对于每一个已注册的proc文件,都建立并维护了一个的proc_dir_entry结构。这个结构非常的重要,对于proc文件系统来说,这个结构是自己的私有数据,相当于其他逻辑文件系统(比如ext2文件系统)在物理硬盘上的索引节点。因此,只有在必要的时候,才会把proc文件系统的proc_dir_entry结构链接到VFS的索引节点中。 因此,proc_read_inode函数的主要目的,是建立一个新的索引节点,只需填充一些基本的信息即可。所以我们能看到proc_read_inode函数非常的简单: static void proc_read_inode(struct inode * inode) inode-i_mtime = inode-i_atime = inode-i_ctime = CURRENT_TIME; 要说明的是,在调用proc_read_inode函数之前,VFS的get_new_inode()函数已为inode设置了其他的基本信息,比如i_sb,i_dev,i_ino,i_flags 及i_count等等。 2 put_inode: force_delete put_inode函数是在索引节点的引用计数减少的时候调用,我们看到,proc文件系统没有实现自己的put_inode函数,而是简单地设置了VFS的force_delete 函数,我们看一下这个函数的内容: 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函数是在引用计数i_count减少之前调用的,因此,对于proc文件系统来说,在每一次inode引用计数减少之前,都要检查引用计数会不会减少至零,如果是,那么就将改索引节点的链接数直接设置为零。 3 delete_inode: proc_delete_inode 当一个索引节点的引用计数和链接数都到零的时候,会调用终极块的delete_inode函数。由于我们使用force_delete实现了proc终极块的put_inode方法,因此我们知道,对于proc文件系统来说,当一个inode的引用计数为零的时候,他的链接数也必为零。 我们看一下该函数的源码: /* * 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); 我们看到,这个函数基本上做了三个工作,首先,将这个索引节点的状态位设置为I_CLEAR,这标志着,这个inode结构已不再使用了。其次,根据这个索引节点的ino号,检查他是否是pid目录中的索引节点,因为pid目录的索引节点号使用 #define fake_ino(pid,ino) (pid)f_type = PROC_SUPER_MAGIC; /* here use the super_blocks 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; 我们看到,他将文件系统的统计数据填充到一个buf中,文件系统类型为PROC_SUPER_MAGIC,在文件系统中的空闲块及文件系统中的文件节点都设置为0,因此对于只存在于内存中的proc文件系统来说,这些统计数据是没有意义的。 (五) 对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 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=%dn, 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函数将该结构注册。 (六) 对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+; 从中我们能看到

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论