一、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 結構的數據結構的遍歷的