[Linux驱动]字符设备驱动学习笔记(二)———实例

本文详细解析了如何在Linux内核中注册字符设备,并深入探讨了内核与用户空间之间的数据读写机制,包括open、read、write等关键函数的实现原理及注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一,注册字符设备

#define GLOBALMEM_MAJOR 256
#define GLOBALMEM_SIZE 0X1000 //4k
static int char_major=GLOBALMEM_MAJOR;//主设备号
struct chartest_dev
{
   struct cdev cdev;
   unsigned char mem[GLOBALMEM_SIZE];
};
struct chartest_dev glob_char_dev;//全局设备结构
static struct class *char_class=NULL;
static struct class_device *char_class_dev=NULL;
//-------------------------------------设备打开函数,匹配上层函数调用
static int char_open(struct inode *inode, struct file *filp)
{
  filp->private_data = &glob_char_dev;
  return 0;
}
//-------------------------------------从内核往用户空间拷贝读取数据
static  ssize_t  char_read(struct file* filp,char __user *buf,size_t count,loff_t *ppos)
{
     	unsigned long p=*ppos;
        int ret=0;
        struct chartest_dev *char_dev=filp->private_data;
        if(p>GLOBALMEM_SIZE)//作越界检查
		return 0;
	if(count>GLOBALMEM_SIZE-p)// 读取数据较多
		count=GLOBALMEM_SIZE-p;
	if(copy_to_user(buf,(void*)(char_dev->mem+p),count))
			ret=-EFAULT;
        else
	{
		*ppos=*ppos+count;
                ret=count;
                printk(KERN_INFO "READ %d BYTES(S) FROM %d",count,p);
	}
        return ret;
}
//-------------------------------------从用户控件往内核写数据
static ssize_t  char_write(struct file* filp,const char __user *buf,size_t count,loff_t *ppos)
{
    	unsigned long p=*ppos;
        int ret=0;
	struct chartest_dev *char_dev=filp->private_data;
        if(p>GLOBALMEM_SIZE)
		return 0;
	if(count>GLOBALMEM_SIZE-p)
		count=GLOBALMEM_SIZE-p;
	if(copy_from_user(char_dev->mem+p,buf,count))
		ret=-EFAULT;
	else
	{
		*ppos=*ppos+count;
                ret=count;
                printk(KERN_INFO "WRITTEN %d BYTES(S) FROM %d",count,p);
	}
        return ret;
}
static loff_t char_llseek(struct file *filp, loff_t offset, int orig)
{
  loff_t ret = 0;
  switch (orig)
  {
    case 0:   
      if (offset < 0)
      {
        ret =  - EINVAL;
        break;
      }
      if ((unsigned int)offset > GLOBALMEM_SIZE)
      {
        ret =  - EINVAL;
        break;
      }
      filp->f_pos = (unsigned int)offset;
      ret = filp->f_pos;
      break;
    case 1:  
      if ((filp->f_pos + offset) > GLOBALMEM_SIZE)
      {
        ret =  - EINVAL;
        break;
      }
      if ((filp->f_pos + offset) < 0)
      {
        ret =  - EINVAL;
        break;
      }
      filp->f_pos += offset;
      ret = filp->f_pos;
      break;
    default:
      ret =  - EINVAL;
      break;
  }
  return ret;
}
static const struct file_operations char_fops = {
	.owner		= THIS_MODULE,
    .llseek     =char_llseek,
    .open		= char_open,
	.read	    = char_read,
	.write	    = char_write,
	};

static void chartest_setup_cdev()
{
    int err,devno=MKDEV(char_major,0);
    cdev_init(&glob_char_dev.cdev,&char_fops);/*初始化*/
    glob_char_dev.cdev.owner=THIS_MODULE;
    err=cdev_add(&glob_char_dev.cdev,devno,1);
    if(err<0)
	printk(KERN_NOTICE "ERROR %d ADDING chartest",err);
    char_class=class_create(THIS_MODULE,"chartest");
    char_class_dev=device_create(char_class,NULL,devno,NULL,"chartest",0);
}
static int  char_init(void)
{
 int result;
 unsigned long char_dev_no=MKDEV(char_major,0);
 if(char_major){
	result=register_chrdev_region(char_dev_no,1,"chartest");
  }
  else{
        result=alloc_chrdev_region(&char_dev_no,0,1,"chartest");
	char_major=MAJOR(char_dev_no);
  }
 printk(KERN_ALERT "Hello,,We succeed\n");
 if(result<0)
	return result;
  chartest_setup_cdev();
  printk(KERN_ALERT "Hello,,We succeed\n");
  return 0;
}
static void  char_exit(void)
{
 	cdev_del(&glob_char_dev.cdev);
 	unregister_chrdev_region( MKDEV(char_major,0),1);//ZHU XIAO
 	device_unregister(char_class_dev);
 	class_destroy(char_class);
 	printk(KERN_ALERT "720 unregister success\n");
}
MODULE_LICENSE("Dual BSD/GPL");
module_init(char_init);

二,注意点:

read和write函数的参数 buff是用户空间的指针,因此内核代码不能直接引用其中的内容,主要原因如下:
1,在内核模式运行时,用户空间的指针可能是无效的
2,用户空间是分页的,涉及的内容可能不在RAM中,对用户空间的内存直接引用可能导致页错误
3.指针是用户空间提供的,可能存在缺陷。

copy_to_user()和copy_from_user()两个函数除了在内核空间和用户空间进行数据拷贝,还会检查用户空间的指针是否有效,如果指针无效则不拷贝,如果拷贝过程中遇到无效地址,则仅仅会复制部分数据,,在这两种情况下返回值是还需要拷贝内存数量的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值