/proc 文件系统是一个虚拟文件系统,通过它可以使用一种新的方法在 Linux® 内核空间和用户空间之间进行通信。在 /proc 文件系统中,我们可以将对虚拟文件的读写作为与内核中实体进行通信的一种手段,但是与普通文件不同的是,这些虚拟文件的内容都是动态创建的.
/proc 文件系统包含了一些目录(用作组织信息的方式)和虚拟文件。虚拟文件可以向用户呈现内核中的一些信息,也可以用作一种从用户空间向内核发送信息的手段。
创建一个/proc文件 在3.8内核之前,使用create_proc_entry创建一个文件,原型如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 struct proc_dir_entry *create_proc_entry ( const char *name, mode_t mode, struct proc_dir_entry *parent ) ;struct proc_dir_entry { const char *name; mode_t mode; uid_t uid; gid_t gid; struct inode_operations *proc_iops ; struct file_operations *proc_fops ; struct proc_dir_entry *parent ; ... read_proc_t *read_proc; write_proc_t *write_proc; void *data; atomic_t count; ... }; void remove_proc_entry ( const char *name, struct proc_dir_entry *parent ) ;struct proc_dir_entry *proc_mkdir (const char *name, struct proc_dir_entry *parent) ;
parent 参数可以为 NULL(表示 /proc 根目录),也可以是很多其他值
proc_dir_entry
在文件系统中的位置
proc_root_fs
/proc
proc_net
/proc/net
proc_bus
/proc/bus
proc_root_driver
/proc/driver
如果我们想创建一个文件为test_modul的虚拟文件,就这样初始化
1 2 3 4 create_proc_entry( "test_module" , 0644 , NULL ); 或者 pt_root = proc_mkdir("test_menu" , NULL ); pt_entry1 = create_proc_entry(USER_ENTRY1, 0666 , pt_root);
/proc文件交互函数 read_proc 供用户读取的函数
1 2 3 4 5 6 7 int mod_read ( char *page, char **start, off_t off, int count, int *eof, void *data ) ;
当需要写入多页数据时(一般一页4Kb),需要用到 start,off.
write_proc 读取用户的输入
1 2 3 4 5 int mod_write ( struct file *filp, const char __user *buff, unsigned long len, void *data ) ;
其它需要用到的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 struct proc_dir_entry *proc_symlink ( const char *name, struct proc_dir_entry *parent, const char *dest ) ;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 ) ;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 ) ;void *vmalloc ( unsigned long size ) ;void vfree ( void *addr ) ;EXPORT_SYMBOL( symbol ); EXPORT_SYMTAB
创建内核驱动 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <linux/module.h> MODULE_LICENSE("GPL" ); int my_module_init ( void ) { printk(KERN_INFO "my_module_init called. Module is now loaded.\n" ); return 0 ; } void my_module_cleanup ( void ) { printk(KERN_INFO "my_module_cleanup called. Module is now unloaded.\n" ); return ; } module_init( my_module_init ); module_exit( my_module_cleanup );
从3.10内核开始,create_proc_entry() 函数被替换成proc_create() 函数, 函数区别如下: 修改前
1 2 3 4 5 6 struct proc_dir_entry *proc_file = create_proc_entry("file" ,0600 ,NULL );if (proc_file) { proc_file->read_proc = file_read; proc_file->write_proc = file_write; proc_file->owner = THIS_MODULE; }
修改后
1 2 3 4 5 6 7 8 struct file_operations proc_fops ={ .read=file_read, .write=file_write, .owner=THIS_MODULE, }; proc_file = proc_create("file" , 0600 , proc_dir, &proc_fops);
编译安装驱动 Makefile
1 2 3 4 5 6 7 obj-m += simple-km.o all: make -C /lib/modules/`uname -r`/build SUBDIRS=$(PWD) modules clean: make -C /lib/modules/`uname -r`/build SUBDIRS=$(PWD) modules
开头的是tab, 不是空格, 一定要注意
执行如下命令
1 2 3 4 5 $ make $ insmod simple-km.ko $ dmesg | tail -5 查看最后5行信息 $ lsmod $ rmmod simple-km.ko
一个简单的示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 #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("VictorV" ); #define MAX_COOKIE_LENGTH PAGE_SIZE static struct proc_dir_entry *proc_entry ;static char *cookie_pot; static int cookie_index; static int next_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; } 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 ; } if (next_fortune >= cookie_index) next_fortune = 0 ; len = sprintf (page, "%s\n" , &cookie_pot[next_fortune]); next_fortune += len; 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 );
效果:
1 2 3 4 5 6 7 8 9 10 11 12 [root@plato] [root@plato] 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]#