在Linux内核源码中,经常要对链表进行操作,其中一个很重要的宏是list_for_each_entry:
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#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))
这个宏本质上是一个for循环,用于遍历链表,利用传入的 pos 作为循环变量,从表头 head 开始,逐项向后(next 方向)移动 pos,直至又回head,循环遍历每一个pos中的member子项。
比如:
int uclass_find_device_by_seq(enum uclass_id id, int seq_or_req_seq,
bool find_req_seq, struct udevice **devp)
{
struct uclass *uc;
struct udevice *dev;
int ret;
*devp = NULL;
pr_debug("%s: %d %d\n", __func__, find_req_seq, seq_or_req_seq);
if (seq_or_req_seq == -1)
return -ENODEV;
ret = uclass_get(id, &uc);
if (ret)
return ret;
list_for_each_entry(dev, &uc->dev_head, uclass_node) {
pr_debug(" - %d %d '%s'\n", dev->req_seq, dev->seq, dev->name);
if ((find_req_seq ? dev->req_seq : dev->seq) ==
seq_or_req_seq) {
*devp = dev;
pr_debug(" - found\n");
return 0;
}
}
pr_debug(" - not found\n");
return -ENODEV;
}
struct udevice {
const struct driver *driver;
const char *name;
void *platdata;
void *parent_platdata;
void *uclass_platdata;
ofnode node;
ulong driver_data;
struct udevice *parent;
void *priv;
struct uclass *uclass;
void *uclass_priv;
void *parent_priv;
struct list_head uclass_node;
struct list_head child_head;
struct list_head sibling_node;
uint32_t flags;
int req_seq;
int seq;
#ifdef CONFIG_DEVRES
struct list_head devres_head;
#endif
};
以上代码就是利用list_for_each_entry()来循环遍历寻找uclass_node成员。
对程序中for循环的三步分析:
我们将for循环分解为一下三点:
-
for循环初始化 pos = list_entry((head)->next, typeof(*pos), member);
-
for循环执行条件 &pos->member != (head);
-
每循环一次执行 pos = list_entry(pos->member.next, typeof(*pos), member))
typeof()是取变量的类型,这里是取指针pos所指向数据的类型。
1、pos = list_entry((head)->next, typeof(*pos), member)
pos相当于循环中返回的循环变量,这里就是返回一个结构体指针。实现过程如下:
函数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) \
container_of(ptr, type, member)
container_of这个函数:这个不做重点分析,这个函数的做用是:就是根据一个结构体变量中的一个域成员变
量的地址来获取指向整个结构体变量的地址。
/**
* 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:作用:根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针。
所以(type *)0)就是将0强转为一个地址,这个地址(0x0000)指向的是类型type的数据。当然,这里是一个技巧,并不是真的在地址0x0000存放了我们的数据。
((type *)0)->member的作用,这里的‘->’很显然是通过指针指取结构体成员的操作。指针就是刚才通过0强转的地址。所以也就是相当于地址0x0000 是结构体类型type的首地址,通过->’取其中的成员变量member。
typeof( ((type *)0)->member ) *__mptr = (ptr):知道member成员的类型,定义一个指针变量__mptr,指向的类型是member的类型,其初始化为ptr的值。
offsetof(type,member): 求出member在结构体中的偏移量
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
根据优先级的顺序,最里面的小括号优先级最高,TYPE *将整型常量0强制转换为TYPE型的指针,且这个指针指向的地址为0,也就是将地址0开始的一块存储空间映射为TYPE型的对象,接下来再对结构体中MEMBER成员进行取址,而整个TYPE结构体的首地址是0,这里获得的地址就是MEMBER成员在TYPE中的相对偏移量。再将这个偏移量强制转换成size_t型数据(无符号整型)。
(char *)__mptr - offsetof(type,member)就是__mptr - offset,即member类型的指针减去member在结构体中的偏移量。
小结:通过上面的分析,和定义出的注释,container_of的作用很明显了 – 获得结构体的地址。那么我们需要给他提供三个参数,分别是:ptr:member成员的指针 type:结构体类型 member:成员member的名字。
这样我们就能通过container_of(ptr, type, member)的返回值,得到结构体的地址。
2、 prefetch(pos->member.next),&pos->member!= (head);
prefetch的含义是告诉cpu那些元素有可能马上就要用到,告诉cpu预取一下,这样可以提高速度,用于预取以提
高遍历速度,&pos->member !=(head) ,这个判断循环条件。
3、 pos= list_entry(pos->member.next, typeof(*pos), member))
和第1实现相似,用于逐项向后(next 方向)移动 pos。