《使用proc文件系统和内核交互》
本文档的Copyleft归rosetta所有,使用GPL发布,可以*拷贝、转载,转载时请保持文档的完整性。
参考资料:IBM developerWorks 《使用/proc文件系统来访问Linux内核的内容》、Linux-2.6.10内核
来源:http://blog.csdn.net/rosetta/article/details/7563610
此篇是在《内核模块编写》和《字符设备驱动程序编写基础》基础上写的,但这篇也是基础文章。使用proc文件系统与内核数据交互和之前写的《字符设备驱动程序编写基础》原理非常相似,只不过前者使用的是内核提供给/proc文件系统的一组专用函数,所以本节主要介绍如何使用这组函数,具体原理学习可参考给出的链接。
可能大家都使用过cat /proc/cpuinfo(meminfo)来查看系统cpu或内存信息,或者也注意到在/proc目录中存在所有以ps -ef进程号为命令的目录,在所有对应的进程号目录中都有许多和该进程相关的文件,具体是些什么文件我不能用最确切的语言来描述或者定义,但我可以肯定的一点是这些文件应该都是由内核里反馈出来的,比如进入某个文件夹,可以cat 对应的status、maps、limits来看看到底包含什么。
下面首先例出这组函数,再给出一个例子。
一、相关函数和结构体
1,struct proc_dir_entry *proc_mkdir(const char *name,
struct proc_dir_entry *parent)//在parent目录创建一个名为name的目录。
比如:struct proc_dir_entry *proc_net_ipsec_dir = proc_mkdir("ipsec", proc_net);//在proc_net目录创建名为ipsec的目录
其中proc_net相当于宏,指/proc/net目录,如果第二个参数传NULL,即指默认/proc目录。以下是几个类似的宏。
proc_root_fs /proc
proc_net /proc/net
proc_bus /proc/bus
proc_root_driver /proc/driver
2,再继续第二个函数之前有必要先说下porc_mkdir的返回值,它是一个结构体struct pro_dir_entry
struct proc_dir_entry {
unsigned int low_ino;
unsigned short namelen;
const char *name;
mode_t mode;
nlink_t nlink;
uid_t uid;
gid_t gid;
unsigned long size;
struct inode_operations * proc_iops;
struct file_operations * proc_fops;
get_info_t *get_info;
struct module *owner;
struct proc_dir_entry *next, *parent, *subdir;
void *data;
read_proc_t *read_proc;
write_proc_t *write_proc;
atomic_t count; /* use count */
int deleted; /* delete flag */
};
里面包含了一个文件(Linux把所有对象都当文件看代,目录当然也是一个文件)所有属性,比如:文件名、权限、uid、gid、前一级目录、包含的子目录等等,我记得在APUE2e在讲解文件系统时讲到过,所以可以结合上面内容进行理解。
这次我们不对这些属性感兴趣,主要是关注下目录的读写函数,即read_proc和write_proc,一会例子中会提到。
3,struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
struct proc_dir_entry *parent)//在parent目录创建一个名为name,权限为mode的文件。
比如:struct proc_dir_entry * item = create_proc_entry("klipsdebug", 0400, "/proc/ipsec")//在/proc/ipsec目录创建一个权限为0400(只读)的文件klipsdebug
4,create_proc_entry执行后返回的proc_dir_entry可以自己指定read、write等函数,如果只需要read函数,可以使用
static inline 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)
它其实是对create_proc_entry进行了封装,把create_proc_read_entry的传入值read_proc赋给了create_proc_entry返回值的read_proc成员,具体可看内核实现。
5,struct proc_dir_entry *proc_symlink( const char *name,
struct proc_dir_entry *parent,
const char *dest );//在parent目录创建指定dest目录的名为name的符号链接。
比如: proc_symlink("ipsec_eroute", proc_net, "ipsec/eroute/all");//在/proc/net创建指向/proc/net/ipsec/eroute/all的符号链接ipsec_eroute。
6,void remove_proc_entry( const char *name, struct proc_dir_entry *parent );//删除parent目录中的名为name的文件
7,还有两上函数比较重要,就是之前提到的read、write函数中需要调用的函数,这两个函数即完成了用户空间和内核空间的数据交互。
unsigned long copy_to_user( void __user *to,
const void *from,
unsigned long n );//
unsigned long copy_from_user( void *to,
const void __user *from,
unsigned long n );
二、给出一个具体的例子以便理解,偷懒起见,直接copy链接处的例子,为保证代码完整性,作者及说明在代码中有所体现。
//proc_study.c
#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 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;
}
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;
}
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 );
//Makefile 自己改的一个Makefile
#ifneq ($(KERNELRELEASE),)
obj-m := my_proc.o
my_proc-objs := proc_study.o
#else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
rm -rf Module.* modules.*
.PHONY: default clean
#endif
编译生成my_proc.ko
下面来测试下:
[root@xxx proc]# ls /proc/fortune -al
-rw-r--r-- 1 root root 0 05-13 16:23 /proc/fortune
[root@xxx proc_study]# insmod my_proc.ko
[root@xxx proc_study]# dmesg
fortune: Module loaded.
[root@xxx proc]# echo "Hello, Just a test" > /proc/fortune
[root@xxx proc]# echo "Secondary test" > /proc/fortune
[root@xxx proc]# ls /proc/fortune -al
-rw-r--r-- 1 root root 0 05-01 07:04 /proc/fortune
[root@xxx proc]# cat /proc/fortune
Secondary test
[root@xxx proc]# cat /proc/fortune
[root@xxx proc_study]# rmmod my_proc
[root@xxx proc_study]# dmesg
fortune: Module loaded.
fortune: Module unloaded.
相关文章
- 20135239 益西拉姆 linux内核分析 使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
- Android通过使用webview实现和js的交互
- Android通过使用webview实现和js的交互
- 使用cat读取和echo写内核文件节点的一些问题
- linux内核分析第四周-使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
- C++windows内核编程笔记day09_day10,对话框和窗体基本控件等的使用
- 如何使用HTML5,Javascript,DOM和CSS创建交互式图像
- 第3阶段——内核启动分析之prepare_namespace()如何挂载根文件系统和mtd分区介绍(6)
- 使用proc文件系统和内核交互
- lua 与 c/c++ 交互(6) lua调用C++(使用数组 和字符串函数)