多次写入字符串到字符设备,一次性读取已经写入的字符串内容


目的:

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");

猜你喜欢

转载自blog.csdn.net/xiaodingqq/article/details/80718944