本文共 4741 字,大约阅读时间需要 15 分钟。
一、头文件:
#include 二、创建与删除proc文件: 创建普通文件: 1.创建不可读写的普通文件 /*此函数只负责在proc中创建一个只读文件,即能在proc中看到这个文件。 * */ struct proc_dir_entry* create_proc_entry (const char *name,mode_t mode,struct proc_dir_entry *parent); @name :要创建的文件名 @mode :要创建的文件的属性 默认0755 @parent :这个文件的父目录 2.创建只读的普通文件 /*和create_proc_entry基本一样。 * 只是为该文件挂接上读函数:read_proc(void data); * */ struct proc_dir_entry * create_proc_read_entry (const char*name,mode_t mode,struct proc_dir_entry *parent,read_proc_t*read_proc,void *data); @name :要创建的文件名 @mode :要创建的文件的属性 默认0755 @parent :这个文件的父目录 @read_proc :当用户读这个文件时,内核调用的函数 @data :传给read_proc的参数 3.创建符号连接 /*该函数在父目录parent下创建一个指向dest的连接name。 * 就像ln: * #ln -s dest parent/name * */ struct proc_dir_entry * proc_symlink (const char *name,struct proc_dir_entry *parent,const char *dest); @name :要创建的文件名 @parent :这个文件的父目录 @dest :符号连接的目标文件 4.创建目录 /*该函数在父目录parent下创建一个目录name * */ struct proc_dir_entry * proc_mkdir (const char *name,struct proc_dir_entry *parent); @name :要创建的目录名 @parent :这个目录的父目录 5.删除文件或目录 /*这个函数从proc文件系统中删除一个文件或目录。 * 注意:1。是通过参数name,而不是通过创建时返回的指针来删除的。 * 2。该函数不会递归删除目录下的文件。 * 3。data变量保存了分配的内存,要先释放对应内存,再删除该文件。 * */ void remove_proc_entry (const char *name,struct proc_dir_entry *parent); @name :要删除的文件或目录名 @parent :所在的父目录 、读写proc文件 为了能让用户读写添加的proc文件,需要挂接上读写回调函数:read_proc和write_proc 例: struct proc_dir_entry * entry; entry->read_proc = read_proc_foo; entry->write_proc = write_proc_foo; 1.读函数read_func /*该函数把要写的信息写入buffer,最多不超过PAGE_SIZE * */ int read_func (char *buffer,char **stat,off_t off,int count,int *peof,void *data); @buffer :把要返回给用户的信息写在buffer里,最大不超过PAGE_SIZE(一般4K) @stat :一般不使用 @off :buffer的偏移量 @count :用户要取的字节数 @peof :读到文件尾时,把peof指向的位置置1 @data :被多个proc文件定义为读时,通过data传递参数 2.写函数write_func /*该函数最多从buffer中读取count个字节的数据。 * 注意:buffer地址在用户空间,需要先用copy_from_user()把这些数据拷贝到内核中。 * */ int write_func (struct file *file,const char *buffer,unsigned long count,void *data); @file :该proc文件对应的file结构,一般忽略。 @buffer :待写的数据所在的位置 @count :待写数据的大小 @data :同read_func 、seq_file编程接口 include/linux/seq_file.h: struct seq_file{ char *buf; size_t size; size_t from; size_t count; loff_t index; loff_t version; struct semaphore sem; struct seq_opertions *op; void *private; }; seq_file结构会在seq_open函数调用中分配,然后作为参数传给每一个seq_file的操作函数。 1.seq_file操作函数: struct seq_operations { void (*start) (struct seq_file *m,loff_t *pos); void (*stop) (struct seq_file *m,void *v); void (*next) (struct seq_file *m,void *v,loff_t *pos); void (*show) (struct seq_file *m,void *v); }; 2.四个操作函数作用: start:用来遍历连接对象的时候做初始化准备工作。 从pos得到偏移,返回一个连接对象的偏移。 也可以返回一个特殊值:SEQ_START_TOKEN 出错返回:ERR_PTR(error_code) stop:遍历结束时被调用。 主要做一些清理工作,如释放锁、释放内存 注意:如果调用了start,就必定会调用stop,即使start返回出错。 next:遍历中寻找下一个连接对象。一般是下一个节点。 show:对当前遍历到的连接对象或节点进行一些操作。 例如调用seq_xxx函数输出对象的一些内容。 3.格式化输出函数 seq_printf(struct seq_file *m,"格式化字符串",var):和printk很相似,只是多了个*m seq_putc (struct seq_file *m,char c);输出字符 seq_puts (struct seq_file *m,const char *s);输出字符串 seq_escape (struct seq_file *m,const char *s,const char*esc);跟seq_puts类似,不同的是这个函数特殊处理字符串s中的特殊字符(esc指针指向的每个字符),这些特殊字符输出为八进制,其他字符照常。
1. 对kern_mout()进行解析:
(1) 调用函数get_unnamed_dev()为文件系统/proc文件系统分配一个设备号。 (2) 调用函数read_super对应的函数proc_read_super()分配super_block,inode,dentry;(a)其中super_block是在函数read_super中新生成的; (b)inode是调用函数proc_get_inde()新生成的; (c)dentry由函数d_alloc_root()创建的; 注:根目录的inode和dentry创建的依据是一个proc_dir_entry结构proc_root,这个结构相当于一般文件系统中硬盘上的(不是内存中的inode或dentry结构)目录项之类的东西。 (3) 调用add_vfsmnt()创建vfsmount结构,同时对其进行设置(如和super_block的关系等)。并使得proc文件系统file_system_type数据结构的kern_mnt指向这个vfsmount结构。但是这并不意味着path_walk()就能顺着路径名“/proc”找到proc文件系统的根节点,因为path_walk()并不涉及file_system_type数据结构,而/proc的inode结构和dentry结构并没有和新创建的这个vfsmount结构联系起来。 2. 所以,光调用kern_mount()还不够,还得由系统调用的初始化进程从内核外部调用系统调用mount(),再安装一次,通常的命令行为:mount –nvt proc/dev/null /proc,也就是说,把建立再“空设备”/dev/null上的proc文件系统安装再节点/proc 上。 在mount()中,因为file_system_type结构中FS-SINGLE标志位为1,所以要调用get_sb_single()来得到相应的超级块,其方法和别的不同(别的都是从设备上读),代码中通过file_system_type结构中的指针kern_mnt取得文件系统vfsmount结构,从而取得其super_block结构,而这个关系正是在kern_mount()中设置好的。 Mount()中其他的操作就和普通的文件系统的安装无异了。这样就把proc文件系统安装到了节点/proc上。 3. 因为整个proc文件系统都不存在于设备上,所以不光是它的根节点需要在内存中创造出来,自根节点以下的所以节点全都需要在运行时加以创建,这是由内核在初始化时调用proc_root_init()完成的。 (1) 首先是直接在/proc下面的叶子节点,即文件节点,这是由proc_misc_init()创建的。 函数proc_misc_init()调用函数create_proc_read_entry()进行分配结构,而create_proc_read_entry()则调用函数create_proc_entry(),下面对函数create_proc_entry()进行解析: a) 调用函数xlate_proc_name()返回父亲节点的proc_dir_entry结构; b) 调用函数kmalloc()分配自己的proc_dir_entry结构; c) 对自己的proc_dir_entry结构进行初始化; d) 调用函数proc_register()对自己的proc_dir_entry结构进行注册登记,即挂到父亲节点的proc_dir_entry结构内的subdir队列中; (2) 创建三个特殊的文件节点,这三个文件是:kmsg、kcore、profile;这些文件的创建直接调用create_proc_entry(); (3) 创建直接在/proc目录中的子目录,如:net,fs,dirver等;这些子目录都是通过proc_mkdir()创建的。 (4) 除了这些子目录之外,就只有/proc/tty是特殊的,因为其他的只有两层目录/proc/dirname,只有/proc/tty是一颗子树,即下面还有目录,所以要专门创建,调用的函数是:proc_tty_init();转载地址:http://aenfb.baihongyu.com/