目的:
做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。
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");