神秘的 container_of() 宏究竟是做什么的? 这个宏定义如下:
#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) \ *__mptr = (ptr); (type *)( (char *)__mptr - offsetof(type,member) );})
为了更好地解释这一点,让我们逐步了解它的作用。在 include/linux/i2c.h 文件中,to_i2c_driver() 宏定义为:
container_of(d, struct i2c_driver, driver)并在代码中被这样使用:
i2c_drv = to_i2c_driver(drv)
其中 dev 是指向 struct device_driver 的指针。
将上述代码替换为第一个宏扩展的结果是:
i2c_drv = container_of(drv, struct i2c_driver, driver);
将上述代码替换为第一个宏扩展的结果是:
i2c_drv = container_of(drv, struct i2c_driver, driver);
然后,下一级别的扩展会生成:
i2c_drv = ({ const typeof( ((struct i2c_driver *)0)->driver) *__mptr = drv; (struct i2c_driver *)( (char *)__mptr - offsetof(struct i2c_driver, driver)); })
为了简化这个过程,请记住 driver 是 i2c_driver 函数中的一个变量。宏的第一行设置一个指针,指向传递给代码的 struct device_driver。宏的第二行找到我们要访问的 struct i2c_driver 在内存中的实际位置。
为了使用实际数字来说明,假设 i2c_driver 结构体看起来像这样:
struct i2c_driver { struct name[32]; };
根据当时编译器使用的打包规则,driver 变量的位置通常在结构体中偏移 32 字节左右。 有关如何查看变量在结构体中位置的更多信息,请参阅我在 2002 年 5 月/6 月号的 Embedded Linux Journal 中发表的文章“编写可移植的设备驱动程序”,网址为 www.linuxjournal.com/article/5783。
所以,如果 drv 指针位于 0xC0101020 位置,则 __mptr 变量会在宏的第一行被赋值为该位置。 然后,从该地址中减去 driver 变量的偏移量,得到值 0xC0101000。 这将被分配给原始赋值左侧的变量 i2c_drv。
为了进行这种指针操作,代码必须知道传递给它的指针类型。 驱动核心只传入注册给它的驱动结构类型,因此这种操作是安全的。 这也防止了内核的其他部分修改用于控制子系统代码的结构的唯一字段。