曲径通幽论坛

标题: container_of 实现原理分析 [打印本页]

作者: beyes    时间: 2009-9-1 22:58
标题: container_of 实现原理分析
container_of() 宏的作用是通过结构体成员的指针找到对应结构体的指针,这个技巧在 linux 内核编程中十分常用。下面以实例来分析一下实现原理。

在 open() 操作里,第一个参数是,struct inode 结构体的指针。在 struct inode 结构体里,有一个 i_cdev 域,这个是一个指向 cdev 结构体的指针。在编写驱动程序时,一般情况下,不会单独的把 cdev 结构体拿出来用,而是将其封装在与驱动程序紧密相联的一个自定义的结构体里。借用 << linux device drivers 3>> 里的这么一种结构体的定义:

struct scull_dev {
            struct scull_qset *data;
            int quantum;
            unsigned long size;
            unsigend int access_key;
            struct semaphore sem;
            struct cdev cdev;
};

下面通过 container_of() 宏来获取 scull_dev 这个结构体的指针:
container_of ( inode->i_cdev, struct scull_dev, cdev);

container_of() 宏中,第 1 个参数就是 struct scull_dev 这种结构体中 cdev 成员的指针;第 2 个参数是 struct_scull 类型结构体;第 3 个参数就是 cdev 结构体。

在 <linux/kernel.h> 头文件中,可以看到 container_of() 宏的定义:
#define container_of(ptr, type, member) ({            \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})
以 struct scull_dev 作为分解宏的参数展开并分析这个宏定义:

(type *)0 欺骗编译器,说 0 地址是一个 struct scull_dev 类型结构体的指针;

((type *)0)->member 指向的就是 c_dev 结构体;

typeof ( ((type *)0)->member) 是取得 member 类型的类型,这里也就是取得 cdev 结构体的类型,即 struct cdev ;

const typeof ( ((type *)0->member) *__mptr  定义了一个 struct cdev 类型的指针 __mptr ;

const typeof ( ((type *)0->member) *__mptr = (ptr) 是将 ptr 指针赋给 __mptr , 这个赋值的目的是要取得 cdev 这个成员定义在 scull_dev 中的位置,这在下面的第 2 条语句中会用到这个位置

第一条语句分析完,下面分析第 2 条语句:

在这条语句中,offsetof(type, member) 是取得 member 参数在 type 类型结构体中的偏移位置。offsetof() 也是一个宏,它的详细情况见http://www.groad.net/bbs/read.php?tid-1040.html
在这里也就是,取得 cdev 成员在 scull_dev 中的偏移。

(char *)__mptr - offsetof(type, member) 是 __mptr 指针往上走 offsetof(type, member) 个偏移量,也就是到达了 scull_dev 这个结构体的头部,所以到此已经获得了 scull_dev 结构体的头地址了。

最后 (type *) ((char *)__mptr - offsetof(type, member)) 是把 scull_dev 结构体头部地址转换为 scull_dev 结构体类型,也就是说至此已经获得了 container_of() 宏中第 2 个参数的指针。




欢迎光临 曲径通幽论坛 (http://www.groad.net/bbs/) Powered by Discuz! X3.2