/proc 虚拟文件系统(实例)发布时间:2022/5/31 13:16:01
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);
删除新建的文件或目录
二.编码
C代码

#ifndef __KERNEL__ #define __KERNEL__ #endif /* __KERNEL__ */ #include #include #include #include #include #include #include #include #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"); [/ol]
三.编译运行
Shell代码

[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 [/ol]
四.接口操作
上面用cat命令直接查看log文件,既然内核提供了通用的read(),write()文件访问接口,那试试。
C代码

#include #include #include #include #include #include #include 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; } [/ol]
运行:
Shell代码

[scada@linux proc_test]$ sudo ./write_test ret:2 read:number:a [/ol]
向/proc/net/test/log写入10后,显示了16进制结果a,测试OK
 |