浅析container_of(
ptr,
type,
member)
文章来源:
http:
//gliethttp.cublog.cn
#
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)
;
//gliethttp_20071018
//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
;
}
|