You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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的嵌入成员,而非指针:

  1. 修改结构体定义:
struct scull_dev{
        struct cdev cdev; // 改为嵌入结构体,而非指针
        int quantum;
        int qset;
        struct scull_qset* qset_entry;
};
  1. 调整初始化函数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)

  1. 修正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实例地址存入其中:

  1. 在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;
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 19:20:32