list_for_each(pos, head)、list_for_each_entry(pos, head, member)

一、list_head

Linux 內核定義了 list_head 數據結構,字段 next 和 prev 分別表示通用雙向鏈表向前和向后的指針元素。不過,值得特別關注的是,list_head 字段的指針中存放的是另一個 list_head 字段的地址,而不是含有 list_head 結構的整個數據結構地址
用 list_head 數據結構構造的一個雙向鏈表如下所示:


這里寫圖片描述

二、list_for_each(pos, head)

list.h

/**
 * list_for_each    -   iterate over a list
 * @pos:    the &struct list_head to use as a loop cursor.
 * @head:   the head for your list.
 */
#define list_for_each(pos, head) \
    for (pos = (head)->next; pos != (head); pos = pos->next)

由上可知,這個宏是對表頭地址 head 指定的鏈表進行掃描,在每次循環時,通過 pos 返回指向鏈表元素的 list_head 結構的指針。這個的實現很簡單,沒有什么需要詳細說明的

三、list_for_each_entry(pos, head, member)

list.h

/**
 * 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_first_entry(head, typeof(*pos), member);    \
         &pos->member != (head);                    \
         pos = list_next_entry(pos, member))

這個宏與 list_for_each 類似,但是返回包含了 list_head 結構的數據結構的地址,而不是 list_head 結構本身的地址

下面就來詳細看一下這是怎么實現的,我們就拿遍歷父進程 father 的 children 鏈表來舉例,那么 head 即為 &father->children,member 即為 sibling

3.1 list_first_entry(ptr, type, member)

list.h

/**
 * list_first_entry - get the first element from a list
 * @ptr:    the list head to take the element from.
 * @type:   the type of the struct this is embedded in.
 * @member: the name of the list_struct within the struct.
 *
 * Note, that list is expected to be not empty.
 */
#define list_first_entry(ptr, type, member) \
    list_entry((ptr)->next, type, member)

結合例子,即相當于 list_entry((&father->children)->next, type, member),所以 for 循環的初始化即為:
pos = list_entry((&father->children)->next, type, member)

3.2 list_entry(ptr, type, member)

list.h

/**
 * 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)

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) );})

即相當于 pos = container_of((&father->children)->next, type, member),那么第一行即為:
const list_head *__mptr = (&father->children)->next;
第二行即為:
(task_struct *)( (char *)__mptr - offsetof(task_struct, sibling) );
即相當于 __mptr 指向的地址減去 sibling 在 task_struct 中的偏移,再轉化為 (task_struct *) 類型的指針
用方程組的思想來理解 container_of 的作用即為:
x->member = ptr,求 x 是多少,代入例子中即為:
x->sibling = (&father->children)->next,返回 x
我們再來看一下 task_struct 結構體中 children 與 sibling 的關系,如下圖所示:

這里寫圖片描述

由上圖可以看出:

  • Child 的 sibling 指向的地址實際在 Parent 的 children 指向的雙向鏈表中
  • 當調用 list_entry((&father->children)->next, type, member) 時,實際就會返回指向 Child1 的指針

3.3 list_next_entry(pos, member)

list.h

/**
 * list_next_entry - get the next element in list
 * @pos:    the type * to cursor
 * @member: the name of the list_struct within the struct.
 */
#define list_next_entry(pos, member) \
    list_entry((pos)->member.next, typeof(*(pos)), member)

由上可以看出 list_next_entry 也是通過 list_entry 實現的,代入例子就是:
list_entry((Child1)->sibling.next, task_struct, sibling)
也就是返回指向 Child2 的指針

綜上所述,list_for_each_entry(pos, head, member) 就是通過這樣的方式實現對包含了 list_head 結構的數據結構的遍歷的

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容