/proc 虚拟文件系统

    Linux下有一个神奇的目录/proc,经常会运行 cat /proc/cpuinfo 命令查看cpu信息,/proc下的确有cpuinfo文件,但是这个文件不是物理存在的,是软件虚拟出来的,与普通文件不同,该文件是动态的。通过/proc可以实现用户态与内核态之间的通信。在内核模式下,可以很方便的创建/proc子目录,并进行读写操作,只不过此时你需要实现文件读写接口,因为内核不知道如何处理该文件。

   下面创建/proc/test目录,并新建log文件,进行读写操作。

一.系统API

extern struct proc_dir_entry proc_mkdir(const char *dir_name,struct proc_dir_entry *parent);

新建/proc子目录,如parent为NULL,则在/proc根下建立目录

extern struct proc_dir_entry proc_create_entry(const char *name,mode_t mode,struct proc_dir_entry *parent);

在/proc下新建虚拟文件

extern void *remove_proc_entry(const char *name,struct proc_dir_entry *parent);

删除新建的文件或目录 

二.编码

#ifndef __KERNEL__
#define __KERNEL__
#endif 	/* __KERNEL__ */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <linux/inet.h>
#include <linux/vmalloc.h>

#define MAX_COOKIE_LENGTH PAGE_SIZE

static struct proc_dir_entry *test_proc_dir;
static struct proc_dir_entry *log_proc_dir;
static char *cookie_pot;	 // 内核缓冲区,用于写数据
static int tValue = 12;		 // 显示值

// 读取日志文件函数
int ProcLogRead( char *buffer,
						char **start,
						off_t offset,
						int length,
						int *eof,
						void *data )
{
	int len;
	if( offset > 0 )
	{
		*eof = 1;
		return 0;
	}

	len = sprintf(buffer, "number:%x\n",tValue);
	return len;
}


// 写日志文件函数
int ProcLogWrite( struct file *filp,
							const char __user *buff,
							unsigned long len,
							void *data)
{
	if( copy_from_user( cookie_pot,buff,len ) )  // 拷贝用户空间值至内核缓冲区
	{
		return -EFAULT;
	}
	
	sscanf(cookie_pot,"%d",&tValue);   	// 保存至全局变量tValue
	printk(KERN_ALERT "%s len:%lu vl:%d\n",cookie_pot,len,tValue);
	return len;
}


static int __init testproc_init(void)
{
	int ret = 0;

	printk(KERN_ALERT "proc test init\n");

	cookie_pot = (char*)vmalloc( MAX_COOKIE_LENGTH );	// 为内核缓冲区分配空间
	if(!cookie_pot)
	{
		ret = -ENOMEM;
	}
	else
	{
		memset(cookie_pot,0,MAX_COOKIE_LENGTH);

		test_proc_dir = proc_mkdir("test",init_net.proc_net); 		// 新建/proc/net/test目录,注:2.6.32以上内核,用init_net.proc_net取代先前的pro_net
		log_proc_dir = create_proc_entry("log",0644,test_proc_dir); 	// 新建文件 /proc/net/test/log

		if(test_proc_dir == NULL
			|| log_proc_dir == NULL)
		{
			ret = -ENOMEM;
			vfree(cookie_pot);
		}
		else
		{
			// 注册读写函数
			log_proc_dir->read_proc = ProcLogRead;
			log_proc_dir->write_proc = ProcLogWrite;
		}
	}

	return 0;
}


static void __exit testproc_exit(void)
{
	printk(KERN_ALERT "clean test proc\n");

	remove_proc_entry("log",test_proc_dir);		// 删除log文件
	remove_proc_entry("test",init_net.proc_net);	// 删除test目录

	vfree(cookie_pot);
}

module_init(testproc_init);
module_exit(testproc_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("kettas");
MODULE_DESCRIPTION("proc test");
MODULE_VERSION("1.0.0");
MODULE_ALIAS("Proc 01");

三.编译运行

[scada@linux proc_test]$ sudo insmod proc_test.ko
[scada@linux proc_test]$ ll /proc/net/test/
总用量 0
-rw-r--r--. 1 root root 0 1月  11 22:14 log
[scada@linux proc_test]$ cat /proc/net/test/log 
number:c

四.接口操作

    上面用cat命令直接查看log文件,既然内核提供了通用的read(),write()文件访问接口,那试试。

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <string.h>

int main(int argc,char **argv)
{
	int fd = open("/proc/net/test/log",O_RDWR,0);
	assert(fd != -1);

	char *vl = "10";  // 此处为字符串 

	// write
	int ret = write(fd,vl,strlen(vl));
	printf("ret:%d\n",ret);

	// read
	char buff[100] = {};
	ret = read(fd,buff,100);
	assert(ret != -1);

	printf("read:%s\n",buff);
	close(fd);

	return 0;
}

运行:

[scada@linux proc_test]$ sudo ./write_test
ret:2
read:number:a

   向/proc/net/test/log写入10后,显示了16进制结果a,测试OK

猜你喜欢

转载自tcspecial.iteye.com/blog/2174780