目的:
做1个虚拟字符设备驱动,可以向该设备多次写入字符串;如果从设备读取,则一次性读取已经写入的字符串内容
写一次内存,分配一块内存。读一次内存,删除那块内存。
效果图:
重要的数据结构:
struct info { struct info *next; //结构体嵌套自身指针 char *msg; }; //头尾单向链表 static struct info *head=NULL; //头节点 static struct info *tail=NULL; //尾节点
框图(流程图):(重要喔!!!结合下图看程序代码,一目了然)
总结:1、刚开始,head和tail指向同一个位置,所以改变了tail->next,也相当于改变head->next。
![](/qrcode.jpg)
2、加入一个节点后,head和tail还是指向同一个位置。
3、链表有2个节点后,head和tail不再指向同一个位置。
核心思想:
1、结构体嵌套自身的指针,头尾单向链表。
2、next嵌套一个节点,然后这个节点的next再嵌套另一个节点。
3、head=head->next,就是不断地找下一个节点的next,然后再找下一个next,直到找到tail->next。
重要的函数:
读函数mem_read:
static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos) { ssize_t retval = 0; int num = MINOR(filp->f_inode->i_rdev); if(num>=MEMDEV_NR_DEVS) return -ENODEV; if(!head) { char info[]="Device is empty, has nothing to read.\n"; if(*ppos>=strlen(info)) { goto over; } if(copy_to_user(buf,info,strlen(info))) { retval = -EFAULT; goto copy_err; } retval=strlen(info); *ppos+=strlen(info); } else //(1) { struct info *node = head; //(2) head = head->next; //(2) if(node->msg) { if(copy_to_user(buf,node->msg,strlen(node->msg))) { retval=-EFAULT; goto copy_err; } retval=strlen(node->msg); kfree(node->msg); //(3) node->msg=NULL; kfree(node); // (3) } } over: return retval; copy_err: return -retval; }
(1) 如果head头部有内容,则进入里面。
(2) node指向head头部,而当前的head指向下一个被读的head->next。
head=head->next,就是不断地找下一个节点的next,然后继续找下一个节点的next,知道找到tail->next。
next:嵌套一个节点,然后这个节点的next再嵌套另一个节点。
(3) 读完那块内存的内容,则释放msg,并且释放msg对应的结构体。
写函数mem_write:
static ssize_t mem_write(struct file *filp,const char __user *buf,size_t size,loff_t *ppos) { size_t count=0; struct info *block=NULL; int num = MINOR(filp->f_inode->i_rdev); if(num>=MEMDEV_NR_DEVS) return -ENODEV; count=(size>MAX_MSG_LEN)?(MAX_MSG_LEN):(size); block=(struct info *)kmalloc(sizeof(struct info),GFP_KERNEL); //(1) if(!block) goto mem_err1; memset(block,0,sizeof(struct info)); // (2) block->msg=(char*)kmalloc(MAX_MSG_LEN,GFP_KERNEL); //(1) if(!block->msg) goto mem_err2; memset(block->msg,0,MAX_MSG_LEN); // (2) if(copy_from_user(block->msg,buf,count)) goto copy_err; if(!head) //(3) head=block; //(3) else tail->next=block; // (3) tail=block; // (3) return count; copy_err: kfree(block->msg); block->msg=NULL; mem_err2: kfree(block); block=NULL; mem_err1: return -EFAULT; }
(1) 为block和block->msg分配内存,因为每一次写入的内容都不同,所以我们为它分配不同的内存空间,每写一次,就分配一块内存。(由外往内分配内存)
(2) 因为分配的内存空间都是肮脏,我们需要为它清理一下。
(3) 如果头部没有内容,则把当前分配的block赋值给head,否则赋值给下一个next,并且赋值给尾部。
完整代码:demo1.c
#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/slab.h> #include <linux/uaccess.h> #define MEMDEV_MAJOR 100 /* 预设的mem的设备号 */ #define MEMDEV_NR_DEVS 2 /* 设备数 */ #define MAX_MSG_LEN 1024 /* max lengh of one message */ static int mem_major = MEMDEV_MAJOR; module_param(mem_major, int, S_IRUGO); struct info { struct info *next; //结构体自身的指针 char *msg; }; //头尾单向链表 static struct info *head=NULL; static struct info *tail=NULL; static struct cdev cdev; /* 文件打开函数 */ int mem_open(struct inode *inode, struct file *filp) { /* 获取设备号 */ int num = MINOR(inode->i_rdev); if(num>=MEMDEV_NR_DEVS) return -ENODEV; return 0; } /* 文件释放函数 */ int mem_release(struct inode *inode,struct file *filp) { /* 获取设备号 */ int num = MINOR(inode->i_rdev); if(num>=MEMDEV_NR_DEVS) return -ENODEV; return 0; } /* 读函数 */ static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos) { ssize_t retval = 0; int num = MINOR(filp->f_inode->i_rdev); if(num>=MEMDEV_NR_DEVS) return -ENODEV; if(!head) { char info[]="Device is empty, has nothing to read.\n"; if(*ppos>=strlen(info)) { goto over; } if(copy_to_user(buf,info,strlen(info))) { retval = -EFAULT; goto copy_err; } retval=strlen(info); *ppos+=strlen(info); } else { struct info *node = head; head = head->next; if(node->msg) { if(copy_to_user(buf,node->msg,strlen(node->msg))) { retval=-EFAULT; goto copy_err; } retval=strlen(node->msg); kfree(node->msg); node->msg=NULL; kfree(node); } } over: return retval; copy_err: return -retval; } /* 写函数 */ static ssize_t mem_write(struct file *filp,const char __user *buf,size_t size,loff_t *ppos) { size_t count=0; struct info *block=NULL; int num = MINOR(filp->f_inode->i_rdev); if(num>=MEMDEV_NR_DEVS) return -ENODEV; count=(size>MAX_MSG_LEN)?(MAX_MSG_LEN):(size); block=(struct info *)kmalloc(sizeof(struct info),GFP_KERNEL); if(!block) goto mem_err1; memset(block,0,sizeof(struct info)); block->msg=(char*)kmalloc(MAX_MSG_LEN,GFP_KERNEL); if(!block->msg) goto mem_err2; memset(block->msg,0,MAX_MSG_LEN); if(copy_from_user(block->msg,buf,count)) goto copy_err; if(!head) head=block; else tail->next=block; tail=block; return count; copy_err: kfree(block->msg); block->msg=NULL; mem_err2: kfree(block); block=NULL; mem_err1: return -EFAULT; } /* 文件操作结构体 */ static const struct file_operations mem_fops = { .owner = THIS_MODULE, .read = mem_read, .write = mem_write, .open = mem_open, .release = mem_release, }; /* 设备驱动模块加载函数 */ static int memdev_init(void) { int result; dev_t devno = MKDEV(mem_major, 0); /* 静态申请设备号 */ if(mem_major) result = register_chrdev_region(devno, 2, "memdev"); else { /* 动态分配设备号 */ result = alloc_chrdev_region(&devno, 0 , 2, "memdev"); mem_major = MAJOR(devno); } if(result<0) return result; /* 初始化cdev结构 */ cdev_init(&cdev, &mem_fops); cdev.owner = THIS_MODULE; cdev.ops = &mem_fops; /* 注册字符设备 */ cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS); printk(KERN_ALERT "major:%i minor:%i\n",mem_major,0); return 0; } /* 模块卸载函数 */ static void memdev_exit(void) { struct info *next = head; if(next) { struct info *pos = next->next; if(next->msg) { kfree(next->msg); next->msg=NULL; } kfree(next); next = pos; } cdev_del(&cdev); /* 注销设备 */ unregister_chrdev_region(MKDEV(mem_major,0), 2); /* 释放设备号 */ } module_init(memdev_init); module_exit(memdev_exit); MODULE_AUTHOR("54geeker"); MODULE_LICENSE("GPL");