使用container_of宏获取数据异常的技术求助
问题分析与解决方案
核心错误在于对container_of宏的适用场景理解偏差,以及结构体设计不符合Linux驱动中设备与cdev关联的常规逻辑:
你的struct scull_dev中cdev是指针类型(struct cdev* cdev),而container_of宏的作用是:已知结构体实例中某个成员的地址,反向推导整个结构体实例的地址。但当前场景下,inode->i_cdev指向的是cdev_alloc()分配的独立cdev对象,这个对象和scull_dev实例只是通过指针关联,并非嵌入在scull_dev中的成员,因此无法通过container_of从cdev对象地址得到scull_dev地址。
解决方法一:修改结构体,将cdev嵌入scull_dev(推荐)
这是Linux字符驱动的标准写法,把cdev作为scull_dev的嵌入成员,而非指针:
- 修改结构体定义:
struct scull_dev{ struct cdev cdev; // 改为嵌入结构体,而非指针 int quantum; int qset; struct scull_qset* qset_entry; };
- 调整初始化函数
init_function,不再需要cdev_alloc():
static int __init init_function(void){ int err; struct scull_dev* sdev; if(scull_major){ dev = MKDEV(scull_major,scull_minor); err = register_chrdev_region(dev,NUMBER_DEVICES,DEVICE_NAME); } else{ err = alloc_chrdev_region(&dev,0,NUMBER_DEVICES,DEVICE_NAME); } if(err){ printk(KERN_WARNING "chrdev region couldn't be allocated!\n"); return err; } sdev = kmalloc(sizeof(struct scull_dev), GFP_KERNEL); if (!sdev) { printk(KERN_WARNING "failed to allocate scull_dev\n"); unregister_chrdev_region(dev, NUMBER_DEVICES); return -ENOMEM; } // 初始化嵌入的cdev cdev_init(&sdev->cdev, &scull_fops); sdev->cdev.owner = THIS_MODULE; sdev->quantum = QUANTUM; sdev->qset = Q_SET; err = cdev_add(&sdev->cdev, dev, NUMBER_DEVICES); if(err){ printk(KERN_WARNING "couldn't add cdev to major/minor\n"); kfree(sdev); unregister_chrdev_region(dev, NUMBER_DEVICES); return err; } allocate_memory(sdev); printk("device got registered and inited. ready for connections ... \n"); return 0; }
(注:可移除单独的skull_setup_cdev函数,或调整该函数适配嵌入的cdev)
- 修正
skull_open中的container_of用法:
static int skull_open(struct inode* inode, struct file* filep){ struct scull_dev* s_dev; printk(KERN_INFO "opened skull device file ... \n"); // inode->i_cdev指向scull_dev中的cdev成员,直接用container_of推导结构体地址 s_dev = container_of(inode->i_cdev, struct scull_dev, cdev); printk("%d,%d\n", s_dev->quantum, s_dev->qset); filep->private_data = s_dev; return 0; }
解决方法二:利用cdev的private_data字段关联(无需修改结构体)
如果不想修改结构体定义,可以借助cdev结构体自带的private_data指针,将scull_dev实例地址存入其中:
- 在
init_function中添加赋值:
kernel_object = cdev_alloc(); if(kernel_object == NULL){ printk(KERN_WARNING "error allocating kernel object\n"); return -1; } // 将sdev地址存入cdev的private_data kernel_object->private_data = sdev;
- 在
skull_open中直接通过private_data获取scull_dev:
static int skull_open(struct inode* inode, struct file* filep){ struct scull_dev* s_dev; printk(KERN_INFO "opened skull device file ... \n"); // 直接从cdev的private_data获取sdev s_dev = inode->i_cdev->private_data; printk("%d,%d\n", s_dev->quantum, s_dev->qset); filep->private_data = s_dev; return 0; }
内容的提问来源于stack exchange,提问作者Whatadodoo
相关产品推荐
相关产品推荐

