read与write方法

在read与write函数中,由于需要在内核空间和用户空间的内存交换数据,
需要借助copy_to_user()与copy_from_user()方法,
这是因为,用户空间的地址有可能指向的位置非内存的位置(当内存空间不足时),这时候内核中直接访问该地址会出错

copy_to_user()就是先将不可访问的地址变成可访问的

#include <linux/module.h>  /*它定义了模块的 API、类型和宏(MODULE_LICENSE、MODULE_AUTHOR等等),所有的内核模块都必须包含这个头文件。*/ 
#include <linux/init.h>
#include <linux/fs.h> //设备号相关函数
#include <linux/slab.h> //内存分配相关函数
#include <linux/types.h>
#include <linux/kdev_t.h>//设备号相关函数
#include <linux/cdev.h>//字符设备头文件
#include <linux/module.h>
#include <linux/uaccess.h>//copy_to_user等函数

struct char_dev
{
	int size;
	char *data;
	struct cdev cdev;//内核中的字符设备
};

int major = 0;
int minor = 0;
struct char_dev char_devices;

//open方法,一般是没啥用的
int char_open(struct inode *inode, struct file *filep)
{
	int Major = 0;
	Major = MAJOR(inode->i_rdev);
	printk("open my_char_dev major: %d\n", Major);
	
	return 0;
}

//简单read方法,只有当要读取的数据大小不大于设备内存时才会读取
ssize_t char_read(struct file *filep, char __user *buf, size_t count, loff_t *offp)
{
	ssize_t returnval = 0;
	printk("read start: char_dev_data:%s, count:%u, offp:%u\n", char_devices.data, (unsigned int)count, (unsigned int)*offp);
	
	if(count > char_devices.size){
		count = char_devices.size;
	}
	
	if(copy_to_user(buf, char_devices.data, count)){
		returnval = -EFAULT;
		goto out;
	}
	
	//*offp += count;
	returnval = count;

out:	
	return returnval;
}

//简单write方法,只有当要读取的数据大小不大于设备内存时才会写入
ssize_t char_write(struct file *filep, const char __user *buf, size_t count, loff_t *offp)
{
	ssize_t returnval = 0;
	printk("write start: buf:%s, count:%u, offp:%u\n", buf, (unsigned int)count, (unsigned int)*offp);
	
	if(count > char_devices.size){
		goto out;
	}
	
	if(copy_from_user(char_devices.data, buf, count)){
		returnval = -EFAULT;
		goto out;
	}
	
	//*offp += count;
	returnval = count;
	printk("write end: char_dev_data:%s\n", char_devices.data);
	
out:
	return returnval;
}

//release方法与open相对应
int char_release(struct inode *inode, struct file *filep)
{
	printk("char release\n");
	return 0;
}


struct file_operations char_fops = {
	.owner	 	= THIS_MODULE,
	.open	 	= char_open,
	.read		= char_read,
	.write		= char_write,
	.release	= char_release,
};




static void char_exit(void) //如果init函数中调用了该函数,则不应有 __exit
{
	dev_t dev;
	printk("char device driver exit \n");
	//释放设备号
	dev = MKDEV(major, minor);
	unregister_chrdev_region(dev, 1);
	printk("release major %d\n", major);
	
	//释放内存
	if(char_devices.data){
		kfree(char_devices.data);
	}
	
	//从内核中删除字符设备
	cdev_del(&(char_devices.cdev));
}

static int __init char_init(void)//__init一个标记,表明是初始化函数
{
	//初始化的代码
	dev_t dev;
	int result;	
	printk("char device driver init \n");
	
	//动态向内核申请设备号	
	result = alloc_chrdev_region(&dev, 0, 1, "my_char_dev");
	major = MAJOR(dev);
	minor = MINOR(dev);
	printk("alloc major %d\n", major);
	if (result < 0) {
		printk(KERN_WARNING "my_char_dev: can't get major %d\n", major);
		return result;
	}
	
	//为设备分配一块内存
	char_devices.size = 100;
	char_devices.data = (char*)kmalloc(char_devices.size, GFP_KERNEL);
	if (!char_devices.data) {
		result = -ENOMEM;
		goto fail;	//不能直接退出函数,需要释放设备号
	}
	
	//向内核中添加字符设备cdev
	cdev_init(&(char_devices.cdev), &char_fops);
	char_devices.cdev.owner = THIS_MODULE;
	char_devices.cdev.ops = &char_fops;
	result = cdev_add(&(char_devices.cdev), dev, 1);
	if((result < 0)) {
		printk(KERN_WARNING "Error %d adding my_char_dev\n", result);
		goto fail;
	}

	return 0; //成功
fail:
	char_exit();
	return result;
}

MODULE_LICENSE("Dual BSD/GPL");
//当模块被加载时,执行moudle_init函数,该函数会调用初始化函数
module_init(char_init);
//模块卸载时,调用,释放资源
module_exit(char_exit); 


有一个疑惑的小地方,
当读的数据比较小时,调用c库的fread函数时,每次read的count参数都是4096
而调用linux的read函数,则不会出现该问题
系统ubuntu14.04

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值