在read与write函数中,由于需要在内核空间和用户空间的内存交换数据,
需要借助copy_to_user()与copy_from_user()方法,
这是因为,用户空间的地址有可能指向的位置非内存的位置(当内存空间不足时),这时候内核中直接访问该地址会出错
当读的数据比较小时,调用c库的fread函数时,每次read的count参数都是4096
而调用linux的read函数,则不会出现该问题
系统ubuntu14.04
需要借助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