教程之家 > 操作系统 > Linux > 正文

使用 /proc 文件系统来访问Linux内核的内容【3】

2014-02-27 11:34  教程之家  侠名  
字号:T|T

我们可以使用 write_proc 函数向 /proc 中写入一项。这个函数的原型如下:

int mod_write( struct file *filp, const char __user *buff,

unsigned long len, void *data );

filp 参数实际上是一个打开文件结构(我们可以忽略这个参数)。buff 参数是传递给您的字符串数据。缓冲区地址实际上是一个用户空间的缓冲区,因此我们不能直接读取它。len 参数定义了在 buff 中有多少数据要被写入。data 参数是一个指向私有数据的指针(参见 清单 7)。在这个模块中,我们声明了一个这种类型的函数来处理到达的数据。

Linux 提供了一组 API 来在用户空间和内核空间之间移动数据。对于 write_proc 的情况来说,我们使用了 copy_from_user 函数来维护用户空间的数据。

读回调函数

我们可以使用 read_proc 函数从一个 /proc 项中读取数据(从内核空间到用户空间)。这个函数的原型如下:

int mod_read( char *page, char **start, off_t off,

int count, int *eof, void *data );

page 参数是这些数据写入到的位置,其中 count 定义了可以写入的最大字符数。在返回多页数据(通常一页是 4KB)时,我们需要使用 start 和 off 参数。当所有数据全部写入之后,就需要设置 eof(文件结束参数)。与 write 类似,data 表示的也是私有数据。此处提供的 page 缓冲区在内核空间中。因此,我们可以直接写入,而不用调用 copy_to_user。

其他有用的函数

我们还可以使用 proc_mkdir、symlinks 以及 proc_symlink 在 /proc 文件系统中创建目录。对于只需要一个 read 函数的简单 /proc 项来说,可以使用 create_proc_read_entry,这会创建一个 /proc 项,并在一个调用中对 read_proc 函数进行初始化。这些函数的原型如清单 8 所示。

清单 8. 其他有用的 /proc 函数

/* Create a directory in the proc filesystem */

struct proc_dir_entry *proc_mkdir( const char *name,

struct proc_dir_entry *parent );

/* Create a symlink in the proc filesystem */

struct proc_dir_entry *proc_symlink( const char *name,

struct proc_dir_entry *parent,

const char *dest );

/* Create a proc_dir_entry with a read_proc_t in one call */

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

/* Copy buffer to user-space from kernel-space */

unsigned long copy_to_user( void __user *to,

const void *from,

unsigned long n );

/* Copy buffer to kernel-space from user-space */

unsigned long copy_from_user( void *to,

const void __user *from,

unsigned long n );

/* Allocate a ‘virtually’ contiguous block of memory */

void *vmalloc( unsigned long size );

/* Free a vmalloc‘d block of memory */

void vfree( void *addr );

/* Export a symbol to the kernel (make it visible to the kernel) */ EXPORT_SYMBOL( symbol );

/* Export all symbols in a file to the kernel (declare before module.h) */ EXPORT_SYMTAB

通过 /proc 文件系统实现财富分发

下面是一个可以支持读写的 LKM。这个简单的程序提供了一个财富甜点分发。在加载这个模块之后,用户就可以使用 echo 命令向其中导入文本财富,然后再使用 cat 命令逐一读出。

清单 9 给出了基本的模块函数和变量。init 函数(init_fortune_module)负责使用 vmalloc 来为这个点心罐分配空间,然后使用memset 将其全部清零。使用所分配并已经清空的 cookie_pot 内存,我们在 /proc 中创建了一个 proc_dir_entry 项,并将其称为fortune。当 proc_entry 成功创建之后,对自己的本地变量和 proc_entry 结构进行了初始化。我们加载了 /proc read 和 write 函数(如清单 9 和清单 10 所示),并确定这个模块的所有者。cleanup 函数简单地从 /proc 文件系统中删除这一项,然后释放cookie_pot 所占据的内存。

cookie_pot 是一个固定大小(4KB)的页,它使用两个索引进行管理。第一个是 cookie_index,标识了要将下一个 cookie 写到哪里去。变量 next_fortune 标识了下一个 cookie 应该从哪里读取以便进行输出。在所有的 fortune 项都读取之后,我们简单地回到了next_fortune。

清单 9. 模块的 init/cleanup 和变量

#include 《linux/module.h》

#include 《linux/kernel.h》

#include 《linux/proc_fs.h》

#include 《linux/string.h》

#include 《linux/vmalloc.h》

#include 《asm/uaccess.h》 MODULE_LICENSE(“GPL”); MODULE_DESCRIPTION(“Fortune Cookie Kernel Module”); MODULE_AUTHOR(“M. Tim Jones”);

#define MAX_COOKIE_LENGTH PAGE_SIZE

static struct proc_dir_entry *proc_entry;

static char *cookie_pot; // Space for fortune strings

static int cookie_index; // Index to write next fortune

static int next_fortune; // Index to read next fortune

int init_fortune_module( void )

{

int ret = 0;

cookie_pot = (char *)vmalloc( MAX_COOKIE_LENGTH );

if (!cookie_pot) {

ret = -ENOMEM;

} else {

memset( cookie_pot, 0, MAX_COOKIE_LENGTH );

proc_entry = create_proc_entry( “fortune”, 0644, NULL );

if (proc_entry == NULL) {

ret = -ENOMEM; vfree(cookie_pot);

printk(KERN_INFO “fortune: Couldn’t create proc entry\n”);

} else {

cookie_index = 0;

next_fortune = 0;

proc_entry-》read_proc = fortune_read;

proc_entry-》write_proc = fortune_write;

proc_entry-》owner = THIS_MODULE;

printk(KERN_INFO “fortune: Module loaded.\n”);

}

}

return ret;

}

void cleanup_fortune_module( void )

{ remove_proc_entry(“fortune”, &proc_root); vfree(cookie_pot);

printk(KERN_INFO “fortune: Module unloaded.\n”);

} module_init( init_fortune_module ); module_exit( cleanup_fortune_module );

向这个罐中新写入一个 cookie 非常简单(如清单 10 所示)。使用这个写入 cookie 的长度,我们可以检查是否有这么多空间可用。如果没有,就返回 -ENOSPC,它会返回给用户空间。否则,就说明空间存在,我们使用 copy_from_user 将用户缓冲区中的数据直接拷贝到 cookie_pot 中。然后增大 cookie_index(基于用户缓冲区的长度)并使用 NULL 来结束这个字符串。最后,返回实际写入cookie_pot 的字符的个数,它会返回到用户进程。

点击加载更多