2007年9月18日星期二

/proc文件系统用于内核调试

/proc文件系统用于内核调试
分类:技术理论时间:2007-7-23 14:02:22作者:mrstrive

一、/proc文件系统简介

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文件输出内核信息。

2seq_file接口

在使用/proc具有很多局限,由于内核分配的内存页只有一页,在读取大文件时接口函数的调用将会很复杂,同时在/proc项的删除时,该文件可能正在被使用。另外,内核不会对同名的文件入口项进行检查,导致无法区分。

使用seq_file将为大的内核虚拟文件提供更简单的接口。seq_file假定我们正在创建的虚拟文件要顺序遍历一个项目序列,这些项目就是要返回给用户空间的信息。

seq_file包括三方面的内容:

l 一组iterator接口,使得可以遍历整个虚拟文件

l 一组便利的格式化输出工具

l 一组封装的file_operation操作,实现了对虚拟文件的大部分操作

我们通过创建iterator来使用相应接口,对项目序列进行遍历,输出所有内容。使用时首先要包含头文件, 之后建立4iterator对象,startnextstopshow

对应的对象为:

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,否则返回错误码。

3seq_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_openseq_file指针存放在file->private_data中。因此ct_open是唯一一个由我们自己定义的file_operation中的操作,其他的readllseekrelease都是由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);

没有评论: