使用 /proc 文件系统来访问 Linux 内核的内容(5)

通过 /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 的字符的个数,它会返回到用户进程。


清单 10. 对 fortune 进行写入操作所使用的函数
ssize_t fortune_write( struct file *filp, const char __user *buff, unsigned long len, void *data ) { int space_available = (MAX_COOKIE_LENGTH-cookie_index)+1; if (len > space_available) { printk(KERN_INFO "fortune: cookie pot is full!\n"); return -ENOSPC; } if (copy_from_user( &cookie_pot[cookie_index], buff, len )) { return -EFAULT; } cookie_index += len; cookie_pot[cookie_index-1] = 0; return len; }  

对 fortune 进行读取也非常简单,如清单 11 所示。由于我们刚才写入数据的缓冲区(page)已经在内核空间中了,因此可以直接对其进行操作,并使用 sprintf 来写入下一个 fortune。如果 next_fortune 索引大于 cookie_index(要写入的下一个位置),那么我们就将 next_fortune 返回为 0,这是第一个 fortune 的索引。在将这个 fortune 写入用户缓冲区之后,在 next_fortune 索引上增加刚才写入的 fortune 的长度。这样就变成了下一个可用 fortune 的索引。这个 fortune 的长度会被返回并传递给用户。


清单 11. 对 fortune 进行读取操作所使用的函数
int fortune_read( char *page, char **start, off_t off, int count, int *eof, void *data ) { int len; if (off > 0) { *eof = 1; return 0; } /* Wrap-around */ if (next_fortune >= cookie_index) next_fortune = 0; len = sprintf(page, "%s\n", &cookie_pot[next_fortune]); next_fortune += len; return len; }  

从这个简单的例子中,我们可以看出通过 /proc 文件系统与内核进行通信实际上是件非常简单的事情。现在让我们来看一下这个 fortune 模块的用法(参见清单 12)。


清单 12. 展示 fortune cookie LKM 的用法
[root@plato]# insmod fortune.ko [root@plato]# echo "Success is an individual proposition. Thomas Watson" > /proc/fortune [root@plato]# echo "If a man does his best, what else is there? Gen. Patton" > /proc/fortune [root@plato]# echo "Cats: All your base are belong to us. Zero Wing" > /proc/fortune [root@plato]# cat /proc/fortune Success is an individual proposition. Thomas Watson [root@plato]# cat /proc/fortune If a man does his best, what else is there? General Patton [root@plato]#  

/proc 虚拟文件系统可以广泛地用来报告内核的信息,也可以用来进行动态配置。我们会发现它对于驱动程序和模块编程来说都是非常完整的。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/26307.html