inux字符驱动之read、write部分

本期主题:
linux字符驱动之read、write部分


往期链接:

  • linux设备驱动中的并发
  • linux设备驱动中的编译乱序和执行乱序
  • linux设备驱动之内核模块
  • linux字符驱动
  • linux字符驱动之ioctl部分

linux字符驱动之read、write部分

  • 1.copy_to_user / copy_from_user
  • 2.测试代码


1.copy_to_user / copy_from_user

由于用户空间并不能直接访问内核空间,所以内核提供了以下两个接口来操作:
copy_from_user():完成从用户空间缓存区到内核空间的复制
copy_to_user():完成从内核空间到用户空间缓存区的复制

函数源码:

static inline int copy_from_user(void *to, const void __user volatile *from,
				 unsigned long n)
{
	__chk_user_ptr(from, n);
	volatile_memcpy(to, from, n);
	return 0;
}

static inline int copy_to_user(void __user volatile *to, const void *from,
			       unsigned long n)
{
	__chk_user_ptr(to, n);
	volatile_memcpy(to, from, n);
	return 0;
}

2.测试代码

写一个驱动测试模块,驱动中写write和read接口,在测试的时候,使用echo/cat来测试从用户空间到内核空间的读写

示例代码:


#include 
#include 
#include 
#include 
#include 
#include 
#include 


MODULE_LICENSE("GPL");
MODULE_AUTHOR("PD");


#define HELLO_TEST_BUFF_SIZE	0x10
//static char hello_test_buf[HELLO_TEST_BUFF_SIZE];
struct hello_dev
{
	char hello_dev_buf[HELLO_TEST_BUFF_SIZE];
};

struct hello_dev *hello_dev_p;

static int hello_open (struct inode *inode, struct file *filep)
{
	filep->private_data = hello_dev_p; //作为驱动的private数据
	printk("hello_open()\n");
	return 0;
}

//param:
//file: 文件结构体指针
//buf: 用户空间内存的地址
//count: 要读的字节数
//ppos: 读的位置相对于文件开头的偏移
static ssize_t hello_read(struct file *file, char __user *buf, size_t count,
			loff_t *ppos)
{
	unsigned long p = *ppos;
	int ret;
	size_t size = count;

	struct hello_dev *dev = file->private_data;

	printk("read func, p is 0x%ld\r\n", p);

	if (p > HELLO_TEST_BUFF_SIZE)
	{
		return 0;
	}
	if (size > HELLO_TEST_BUFF_SIZE - p)
	{
		size = HELLO_TEST_BUFF_SIZE - p;
	}

	if (copy_to_user(buf, dev->hello_dev_buf + p, size))
	{
		ret = -EFAULT;
		printk("read failed\r\n");
	}
	else
	{
		*ppos += count;
		ret = count;

		printk("read %u bytes from kernel\r\n", size);
	}
	return ret;
}

static ssize_t hello_write(struct file *file, const char __user *buf, size_t count,
			loff_t *ppos)
{
	unsigned long p = *ppos;
	int ret;
	struct hello_dev *dev = file->private_data;
	printk("write func, p is 0x%ld\r\n", p);

	if (copy_from_user(dev->hello_dev_buf + p, buf, count))
	{
		ret = -EFAULT;
		printk("write failed\r\n");
	}
	else
	{
		*ppos += count;
		ret = count;

		printk("write %u bytes to kernel\r\n", count);
	}
	return ret;
}

static struct file_operations hello_ops = 
{
	.open = hello_open,
	.read = hello_read,
	.write = hello_write,
};

static int major = 230;
static int minor = 0;
static dev_t dev_num = 0;
static struct cdev cdev;

static int hello_init(void)
{
	int result;
	int error;

	dev_num = MKDEV(major, minor);
	
	result = register_chrdev_region(dev_num, 1, "test");

	if (result < 0)
	{
		printk("register_chrdev_region fail \n");
		return result;
	}
	printk("hello_init, register_chrdev_region OK \n");

	cdev_init(&cdev,&hello_ops);
	error = cdev_add(&cdev,dev_num,1);
	//添加hello_dev_p的kzalloc
	hello_dev_p = kzalloc(sizeof(struct hello_dev), GFP_KERNEL);
	if (!hello_dev_p)
	{
		result = -ENOMEM;
		printk("kzalloc fail \n");
		return result;
	}

	if(error < 0)
	{
		printk("cdev_add fail \n");
		unregister_chrdev_region(dev_num,1);
		return error;
	}
	return 0;
}


static void hello_exit(void)
{
	printk("hello_exit \n");
	cdev_del(&cdev);
	unregister_chrdev_region(dev_num, 1);
	return;
}

module_init(hello_init); //insmod
module_exit(hello_exit);//rmmod

测试结果:
在这里插入图片描述
inux字符驱动之read、write部分_第1张图片

你可能感兴趣的:(linux设备驱动开发,驱动开发,linux,运维)