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