container_of宏 相关

 

container_of宏,它的功能是得到包含某个结构成员的结构的指针:

其实现如下:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({                      /
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    /
        (type *)( (char *)__mptr - offsetof(type,member) );})

 

ptr是指向结构体成员的指针
type是结构体的类型,例如struct abc
member是成员的变量名

1、typeof( ((type *)0)->member ):取得成员的类型
2、const typeof( ((type *)0)->member ) *__mptr = (ptr); :定义指向成员类型的指针变量__mptr,并将第一个参数(指向成员的指针)赋值给
__mptr
3、offsetof(type,member) :
计算结构体中成员变量相对于结构体指针的偏移量
4、
(TYPE *)0)->MEMBER:起始地址为0的结构体中成员变量的地址,也就是成员的偏移值

PS:这个实现最巧妙的地方在于
“(type *)0”的应用,将地址0强行转换成指向某个类型的指针


    分析可知__mptr指向的是一个type结构里typeof(((type *)0)->member)类型member成员的指针,offsetof(type,member)是这个成员在结构中的偏移,单位是字节,所以为了计算type结构的起始地址,__mptr减去它自己的偏移。

 

类似的宏在Windows下的定义:

#define FIELD_OFFSET(type, field)    ((LONG)(LONG_PTR)&(((type *)0)->field))

#define CONTAINING_RECORD(address, type, field) ((type *)( /
                                                  (PCHAR)(address) - /
                                                  (ULONG_PTR)(&((type *)0)->field)))

 

记得WINDOWS DDK中也有这个的一个宏,该宏的功能就是在知道一个结构中的成员的地址和对应的成员名字以及这个结构的类型的情况下获取这个结构的指针。

关于container_of见kernel.h中:
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr:     the pointer to the member.
* @type:     the type of the container struct this is embedded in.
* @member:     the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({             /
         const typeof( ((type *)0)->member ) *__mptr = (ptr);     /
         (type *)( (char *)__mptr - offsetof(type,member) );})
container_of在Linux Kernel中的应用非常广泛,它用于获得某结构中某成员的入口地址.

关于offsetof见stddef.h中:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
TYPE是某struct的类型 0是一个假想TYPE类型struct,MEMBER是该struct中的一个成员. 由于该struct的基地址为0, MEMBER的地址就是该成员相对与struct头地址的偏移量.
关于typeof,这是gcc的C语言扩展保留字,用于声明变量类型.
const typeof( ((type *)0->member ) *__mptr = (ptr);意思是声明一个与member同一个类型的指针常量 *__mptr,并初始化为ptr.
(type *)( (char *)__mptr - offsetof(type,member) );意思是__mptr的地址减去member在该struct中的偏移量得到的地址, 再转换成type型指针. 该指针就是结构的的入口地址了.
例如:
/*
 priv是usb_hcd结构中的hcd_priv指针,通过这个宏得到这个成员变量所在的 结构usb_hcd的指针
 */
 return container_of((void *) priv, struct usb_hcd, hcd_priv);

 

指针ptr指向结构体type中的成员member;通过指针ptr,返回结构体type的起始地址


#define container_of(ptr, type, member) ({                  /
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    /
    (type *)( (char *)__mptr - offsetof(type,member) );})
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
1.ptr为物理地址,其类型和member类型一致,最终使用typeof( ((type *)0)->member )
  由编译器自动返回member的类型
2.type为包含member成员的结构体
3.offsetof(type,member)为member成员在type结构体中的偏移值,大小范围0~sizeof(type)字节 (因为以0地址为type类型数据结构的起始地址)
4.ptr- offsetof()就等于包含该ptr的type结构体父变量的物理起始地址,强制转换为(type*).

应用举例:

#define list_entry(ptr, type, member)                         /
    container_of(ptr, type, member)

#define list_for_each_entry(pos, head, member)                /
    for (pos = list_entry((head)->next, typeof(*pos), member);/
     prefetch(pos->member.next), &pos->member != (head);      /
     pos = list_entry(pos->member.next, typeof(*pos), member))
//-------------------------------------------------------------
list_entry((head)->next, typeof(*pos), member)返回(head)->next物理指针所处位置向前减去offsetof()个字节数据之后, 其父变量pos的物理地址,父变量的类型在编译时由typeof(*pos)自动返回(gliethttp).
所以list_for_each_entry遍历head下面挂接的类型为typeof(*pos)的childs结构体们,当然每个child结构体包含struct list_head node之类相似的双向链表list_head类型项,就这样通过循环pos将依次指向双向链表上的各个child.(member就是child类型中被定义的变量名)
//-------------------------------------------------------------
下面一段程序摘自: drivers/usb/driver.c
struct usb_dynids {
    spinlock_t lock;
    struct list_head list;
};
struct usb_dynid {
    struct list_head node;
    struct usb_device_id id;
};
static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *intf,
                            struct usb_driver *drv)
{
    struct usb_dynid *dynid;

    spin_lock(&drv->dynids.lock);
//1. drv->dynids.list为head,即:树根,父亲,正如上面的struct usb_dynids
//2. dynid为child,其中drv->dynids.list.next存放了第一个child结构体中的
//   struct list_head类型名字为node的物理地址值.
//3. 看着很复杂,其实翻译出来就简单多了(gliethttp)
//模型为for(child;child != head;child = child->next)
// for(dynid = container_of(drv->dynids.list.next, struct usb_dynid,nod);
//     dynid->node != &drv->dynids.list;
//     dynid = container_of(dynid->node.next, struct usb_dynid,nod)
//     )
    list_for_each_entry(dynid, &drv->dynids.list, node) {
        if (usb_match_one_id(intf, &dynid->id)) {
            spin_unlock(&drv->dynids.lock);
            return &dynid->id;
        }
    }
    spin_unlock(&drv->dynids.lock);
    return NULL;
}
 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值