Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2335385
  • 博文数量: 168
  • 博客积分: 3184
  • 博客等级: 中校
  • 技术积分: 3959
  • 用 户 组: 普通用户
  • 注册时间: 2009-05-12 18:57
文章存档

2019年(1)

2018年(2)

2017年(3)

2016年(15)

2015年(10)

2014年(60)

2011年(1)

2010年(76)

分类: LINUX

2010-11-11 11:56:35

一、/proc文件系统简介
Linux
提供了一个特殊的文件系统——/proc,通过建立内核与进程之间发送信息的机制,使得可以在进程运行时动态地读写内核内部的数据结构、改变内核设置。与其他文件系统的不同之处在于,/proc是处于内存之中的。

/proc
中的每个文件都绑定于一个内核函数,当用户读取某个文件时,将调用指定函数读取所需信息返回给用户空间,对于内核模块调试而言,需要查看内核所处的状态等信息,此时将可以通过在/proc下创建对应文件,通过读取该文件来及时返回指定内核模块信息。由于大多数/proc文件是只读项,这里介绍只读的情况。

二、使用/proc文件系统

1
、传统/proc接口

使用/proc时需要包含头文件,首先需要创建一个函数,使得进程读取指定文件时,可以通过该函数来返回指定信息,该函数称为read_proc方法。当读取文件时,内核将分配一个内存页,该内存页指针将作为参数传递给read_proc方法,在方法中填写所需内容到缓冲区中,最后返回给用户。 Read_proc定义:

QUOTE:

 

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入口项连接起来,其接口函数为:(此处所谓的入口项,类比于文件节点-mknod得到的)

QUOTE:

 

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来使用相应接口,对项目序列进行遍历,输出所有内容。使用时首先要包含头文件, 之后建立4iterator对象,startnextstopshow

对应的对象为:

QUOTE:

 

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已满无法继续写入数据。

对于直接输出字符,可以使用:

QUOTE:

 

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操作来完成:

QUOTE:

 

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应该定义为:

QUOTE:

 

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来实现,如:

QUOTE:

 

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);

阅读(1459) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~