Linux提供了一个特殊的文件系统----/proc,通过建立内核与进程之间发送信息的机制,使得可以在进程运行时动态地读写内核内部的数据结构、改变内核设置。与其他文件系统的不同之处在于,/proc是处于内存之中的。
/proc中的每个文件都绑定于一个内核函数,当用户读取某个文件时,将调用指定函数读取所需信息返回给用户空间,对于内核模块调试而言,需要查看内核所处的状态等信息,此时将可以通过在/proc下创建对应文件,通过读取该文件来及时返回指定内核模块信息。由于大多数/proc文件是只读项,这里介绍只读的情况。
二、使用/proc文件系统
1、传统/proc接口
使用/proc时需要包含头文件,首先需要创建一个函数,使得进程读取指定文件时,可以通过该函数来返回指定信息,该函数称为read_proc方法。当读取文件时,内核将分配一个内存页,该内存页指针将作为参数传递给read_proc方法,在方法中填写所需内容到缓冲区中,最后返回给用户。Read_proc定义:
引用:Int (*read_proc)(char *page, char **start, off_t offset, int count, int *eof, void *data);
其中page是所分配的内存页,start将指示实际数据写到的内存页的位置,offset指示读取的虚拟文件的位置,count是读取的字节数,eof是一个简单的标志,data用于内部记录。
一旦定义好了read_proc函数,就应该将其与一个/proc入口项连接起来,其接口函数为:
引用:struct proc_dir_entry * create_proc_read_entry(const char *name, mode_t mode, struct proc_dir_entry *base, read_proc_t *read_proc,void *data);
通过该函数,可以在指定目录下(/proc或者其某个子目录)创建一个名为name的文件,其中的read_proc就是开始所编写的函数,调用该函数后,read_proc就与name文件结合起来了,当查看name文件时将调用read_proc文件输出内核信息。
2、seq_file接口
在使用/proc具有很多局限,由于内核分配的内存页只有一页,在读取大文件时接口函数的调用将会很复杂,同时在/proc项的删除时,该文件可能正在被使用。另外,内核不会对同名的文件入口项进行检查,导致无法区分。
使用seq_file将为大的内核虚拟文件提供更简单的接口。seq_file假定我们正在创建的虚拟文件要顺序遍历一个项目序列,这些项目就是要返回给用户空间的信息。
seq_file包括三方面的内容:
一组iterator接口,使得可以遍历整个虚拟文件
一组便利的格式化输出工具
一组封装的file_operation操作,实现了对虚拟文件的大部分操作
我们通过创建iterator来使用相应接口,对项目序列进行遍历,输出所有内容。使用时首先要包含头文件, 之后建立4个iterator对象,start、next、stop和show。
对应的对象为:
引用: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);
int (*show) (struct seq_file *m, void *v);
}
其中start方法用pos作参数,将返回一个iterator对象,表示的是文件读取的起始位置。如果指定的位置超过文件末尾,应当返回NULL。对于一些复杂的应用程序,seq_file结构中的private成员将被使用。start函数还有一个特殊的返回值----SEQ_START_TOKEN,当在show函数之前想打印出一个头部信息时可以使用该标志。
next()函数任务是将iterator的位置前进到下一项,将返回一个iterator对象,如果已到达序列末尾将返回NULL。
当遍历过程完成时将调用stop函数,已完成清除工作。比如使用了动态内存进行分配时在此处可以完成内存释放工作。
show函数将当前iterator对象所指向的对象以格式化形式进行输出。正常完成时将返回0,否则返回错误码。
3、seq_file格式化输出
为了将信息输出到用户空间,定义了一组格式化的输出工具,其中最常用的即是seq_printf(),类似于printk的功能,但使用seq_file指针作为参数。通常并不会检查其返回值,但是如果返回值为非零时则说明发生了错误,如buffer已满无法继续写入数据。
对于直接输出字符,可以使用:
引用:int seq_putc(struct seq_file *m, char c);
int seq_puts(struct seq_file *m, const char *s);
int seq_escape(struct seq_file *m, const char *s, const char * esc);
4、使用seq_file连接/proc
以上完成了具体操作的定义,下面应该将其与具体的/proc下的文件连接起来。首先创建一个file_operation结构,该结构封装了在/proc所需的必要操作。为了将这些操作与文件连接起来,首先要使用对应的open操作来完成:
引用:Static int ct_open(struct inode *inode, struct file *file)
{Return seq_open(file, &ct_seq_ops);}
在成功完成该调用后,seq_open将seq_file指针存放在file->private_data中。因此ct_open是唯一一个由我们自己定义的file_operation中的操作,其他的read,llseek,release都是由seq_file代码本身实现的,即file_operation应该定义为:
引用:static struct file_operations ct_file_ops = {
.owner = THIS_MODULE,
.open = ct_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release};
最后,则是创建/proc文件本身,通过使用create_proc_entry来实现,如:
引用:static int ct_init(void){
struct proc_dir_entry * entry;
entry = create_proc_entry(“sequence”, 0, NULL);
if(entry)
entry->proc_fops = &ct_file_ops;
return 0;}
module_init(ct_init);