初识内核链表
前面我們說過如何用C實現通用類型的鏈表,比如void*的指針,零長數組等。可是小菜鳥畢竟趕不上大師,還是Linux內核巧妙啊,這里面的鏈表,才是鏈表中的“奇葩”。
源碼的路徑是include/linux/list.h
我們先找幾個簡單的讀一讀吧。
struct list_head { struct list_head *next, *prev; };這個是在include/linux/types.h中定義的。雖然名字叫list_head,讓人感覺是頭節點,其實這就是通用的節點,和頭一點關系都沒有。你也許會奇怪,怎么沒有數據域?是的,就是沒有數據域。那數據怎么辦?后面你就知道了,這個節點是內嵌在用戶數據中的。
static inline void INIT_LIST_HEAD(struct list_head *list) {list->next = list;list->prev = list; }
節點的初始化函數,自己指向自己。
static inline void __list_add(struct list_head *new,struct list_head *prev,struct list_head *next) {next->prev = new;new->next = next;new->prev = prev;prev->next = new; }在兩個節點中間插入一個新節點。
static inline void list_add(struct list_head *new, struct list_head *head) {__list_add(new, head, head->next); }假如head是頭結點的話,這個就是頭插了。
static inline void list_add_tail(struct list_head *new, struct list_head *head) {__list_add(new, head->prev, head); }head->prev就是最后一個節點了,這個節點的后面那個就是head,所以這個函數是尾插。
static inline void __list_del(struct list_head * prev, struct list_head * next) {next->prev = prev;prev->next = next; }刪除掉一個節點(必須知道這個節點的前驅和后繼)
static inline void list_del(struct list_head *entry) {__list_del(entry->prev, entry->next);}刪除某個節點。
static inline int list_empty(struct list_head *head) {return head->next == head; }判斷鏈表是不是為空,這里的head是頭結點。
/*** 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)這是遍歷。
/*** list_for_each_safe - iterate over a list safe against removal of list entry* @pos: the &struct list_head to use as a loop cursor.* @n: another &struct list_head to use as temporary storage* @head: the head for your list.*/ #define list_for_each_safe(pos, n, head) \for (pos = (head)->next, n = pos->next; pos != (head); \pos = n, n = pos->next)安全遍歷,看注釋就明白,這個在遍歷的過程中刪除節點也是可以用的,因為當前節點的下一個已經保存了哦。
/*** 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換了個馬甲,叫
list_entry, ptr是指向list_head 的指針(簡稱小指針),type是結構體的類型,比如struct XXXX, member 是list_head 在結構體里的名字。最后就得到了指向struct XXXX的指針(簡稱大指針)。我們把這個宏叫做“小指針轉大指針”。
/*** 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)ptr在這里表示頭指針,這個宏得到了第一個結構體的地址(大指針)。
/*** 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)pos在這里是一個結構體的指針(大指針),這個宏得到了下一個結構體的地址。
/*** 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))遍歷鏈表中的每個大指針。
pos:大指針,head:頭結點,member:list_head 在結構體中的名字
基本的就說到這里,下面我們寫個例子。
#include <stdio.h> #include "list.h"struct person {char name[12];struct list_head list;char sex;unsigned char age; };int main() {struct person persons[] = {{"Marry", NULL,NULL,'f',20},{"Mike",NULL,NULL,'m',23},{"Leslie",NULL,NULL,'m', 36},{"Ann",NULL,NULL,'f',27}};LIST_HEAD(head);//頭結點int i;for (i = 0; i < sizeof(persons)/sizeof(persons[0]); ++i) {list_add(&persons[i].list, &head);}struct list_head *cur = NULL;struct person *pdata = NULL;list_for_each(cur, &head) {pdata = container_of(cur, struct person, list);printf("%s:%d\n", pdata->name, pdata->age);}printf("list_for_each_entry\n"); list_for_each_entry(pdata, &head, list) {printf("%s:%d\n", pdata->name, pdata->age);}return 0; }
運行結果:
Ann:27
Leslie:36
Mike:23
Marry:20
list_for_each_entry
Ann:27
Leslie:36
Mike:23
Marry:20
(完)
總結
- 上一篇: 2020快手用户及营销报告
- 下一篇: 二叉查找树的C语言实现(一)