浅析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; }
|