list_entry 解析

list_entry的宏定义:
#define list_entry(ptr, type, member) \ 
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) 
这个倒是不难理解:从一个结构的成员指针找到其容器的指针。
但是正因为如此,我的第一感觉是,这个宏的名字应该更加抽象,名字似乎应该改称叫“寻找容器”一类的,查看list.h源代码,发现现在的定义是这样的:
#define list_entry(ptr, type, member) 
    container_of(ptr, type, member)
#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)
linux不想用C++,但又想利用C++的优点,如是出现了很多奇怪的宏,他们叫做trick。
ptr是找容器的那个变量的指针,把它减去自己在容器中的偏移量的值就应该得到容器的指针。(容器就是包含自己的那个结构)。指针的加减要注意类型,用(char*)ptr是为了计算字节偏移。((type *)0)->member是一个小技巧。自己理解吧。前面的(type *)再转回容器的类型。
=====================
#define list_entry(ptr, type, member) \
        ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))  
  ptr是指向list_head类型链表的指针,type为一个结构,而member为结构type中的一个域,类型为list_head,这个宏返回指向type结构的指针。在内核代码中大量引用了这个宏,因此,搞清楚这个宏的含义和用法非常重要。 
设有如下结构体定义:typedef struct xxx
{
     ……(结构体中其他域,令其总大小为size1)
     type1 member;
     ……(结构体中其他域)
}type;定义变量:   type a;
   type * b;
   type1 * ptr;执行:   ptr=&(a.member);
   b=list_entry(ptr,type,member);则可使b指向a,得到了a的地址。如何做到的呢?先看&((type *)0)->member:把“0”强制转化为指针类型,则该指针一定指向“0”(数据段基址)。因为指针是“type *”型的,所以可取到以“0”为基地址的一个type型变量member域的地址。那么这个地址也就等于member域到结构体基地址的偏移字节数。

 


再来看 ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))):(char *)(ptr)使得指针的加减操作步长为一字节,(unsigned long)(&((type *)0)->member)等于ptr指向的member到该member所在结构体基地址的偏移字节数。二者一减便得出该结构体的地址。转换为 (type *)型的指针,大功告成。

==============

 list_entry定义如下:/*** list_entry - get the struct for this entry* @ptr:        the &struct list_head pointer.* @type:        the type of the struct this is embedded in.* @member:        the name of the list_struct within the struct.*/#define list_entry(ptr, type, member) \        ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))===================
在yaffs的这里,之所以用到这样的语句,就是因为他使用的双链表:
在新的yaffs中此处改为:
#define list_entry(entry, type, member)  ((type *)((char *)(entry)-(unsigned long)(&((type *)NULL)->member)))
使用的地方:
lh = allocator->free_objs.next;
obj = list_entry(lh, struct yaffs_obj, siblings);
list_del_init(lh);
allocator->n_free_objects--;

这里看来是想获取yaffs_obj的结构体!
而allocator的结构体为:
struct yaffs_allocator {
	int n_tnodes_created;
	struct yaffs_tnode *free_tnodes;
	int n_free_tnodes;
	struct yaffs_tnode_list *alloc_tnode_list;

	int n_obj_created;
	  该处用来链接所有空闲的yaffs_obj 模块,
	int n_free_objects;

	struct yaffs_obj_list *allocated_obj_list;
};

list_head 内容:
struct list_head {
	struct list_head *next; /* next in chain */
	struct list_head *prev; /* previous in chain */
};

看到这里这个双链表,再来看 yaffs_obj 结构体,  

struct yaffs_obj {
	u8 deleted:1;		/* This should only apply to unlinked files. */
	u8 soft_del:1;		/* it has also been soft deleted */
..................省略
	struct list_head hash_link;	/* list of objects in hash bucket */

	struct list_head hard_links;	/* hard linked object chain*/

	/* directory structure stuff */
	/* also used for linking up the free list */
	struct yaffs_obj *parent;
	struct list_head siblings;
.....................省略
	enum yaffs_obj_type variant_type;

	union yaffs_obj_var variant;

};
 
  
siblings是兄弟姐妹的意思,结构体yaffs_obj 中通过 siblings 将所有的 yaffs_obj 链接在一起,但是 此处只知道siblings的地址,要获取yaffs_obj 地址只有 list_entry 那样进行 减去siblings的偏移量了。

再来看源程序:
lh = allocator->free_objs.next;
取到 yaffs_obj 的成员siblings的地址
obj = list_entry(lh, struct yaffs_obj, siblings);
由该地址求的yaffs_obj的地址,其实就是这么简单。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值