|
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 个参数的指针。 |
|