第一次阅读源码,选择的是搜狗的workflow,这次阅读的是list
内核链表的结构 1 2 3 struct list_head { struct list_head *next, *prev; };
这里发现,链表中没有数据,只有前驱和后继指针
如果我们要使用到内核的链表结构,我们需要定义一个struct list_head{}类型的结构体成员对象就可以了
eg:
1 2 3 4 struct Task { struct list_head entry_list; int val; }
链表初始化 这里我只看第二种,即INIT_LIST_HEAD
1 2 3 4 5 static inline void INIT_LIST_HEAD (struct list_head *list) { list->next = list; list->prev = list; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include "list.h" struct Task { struct list_head entry_list; int val; }; int main () { struct Task task; INIT_LIST_HEAD (&task.entry_list); return 0 ; }
链表增加节点:list_add 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 static inline void __list_add(struct list_head *node, struct list_head *prev, struct list_head *next) { next->prev = node; node->next = next; node->prev = prev; prev->next = node; } static inline void list_add (struct list_head *node, struct list_head *head) { __list_add(node, head, head->next); } static inline void list_add_tail (struct list_head *node, struct list_head *head) { __list_add(node, head->prev, head); }
list_add
是头插法 list_add_tail
是尾插法
__list_add
的作用就是将第一个参数node,插到第二个参数和第三个参数之间
测试用例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include "list.h" #include <stdlib.h> #include <stdio.h> struct Task { struct list_head entry_list; int val; }; void list_add_test () { struct Task task; INIT_LIST_HEAD (&task.entry_list); struct list_head *pos; for (int i = 0 ; i < 4 ; i++) { struct Task *t = (struct Task *)malloc (sizeof (struct Task)); t->val = i; list_add (&t->entry_list, &task.entry_list); } list_for_each (pos, &task.entry_list) { printf ("val = %d\n" , ((struct Task*)pos)->val); } } void list_tail_test () { struct Task task; INIT_LIST_HEAD (&task.entry_list); struct list_head *pos; for (int i = 0 ; i < 4 ; i++) { struct Task *t = (struct Task *)malloc (sizeof (struct Task)); t->val = i; list_add_tail (&t->entry_list, &task.entry_list); } list_for_each (pos, &task.entry_list) { printf ("val = %d\n" , ((struct Task*)pos)->val); } } int main () { printf ("list_add_test : \n" ); list_add_test (); printf ("list_tail_test : \n" ); list_tail_test (); return 0 ; }
其中的list_for_each函数很简单,这里先贴一下,就是遍历
1 2 #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next)
1 2 3 4 5 6 7 8 9 10 11 hoshea@hoshea-pc://home/hoshea/Desktop/workflow/workflow-0.9.0/test/mytest/list_test$ ./a.out list_add_test : val = 3 val = 2 val = 1 val = 0 list_tail_test : val = 0 val = 1 val = 2 val = 3
删除节点:list_del 1 2 3 4 5 6 7 8 9 10 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); }
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include "list.h" #include <stdlib.h> #include <stdio.h> struct Task { struct list_head entry_list; int val; }; int main () { struct Task task; INIT_LIST_HEAD (&task.entry_list); struct list_head *pos; for (int i = 0 ; i < 4 ; i++) { struct Task *t = (struct Task *)malloc (sizeof (struct Task)); t->val = i; list_add (&t->entry_list, &task.entry_list); } list_for_each (pos, &task.entry_list) { printf ("val = %d\n" , ((struct Task*)pos)->val); } struct Task *task_pos; list_for_each_entry (task_pos, &task.entry_list, entry_list) { if (task_pos->val == 2 ) { list_del (&task_pos->entry_list); break ; } } printf ("After del : \n" ); list_for_each (pos, &task.entry_list) { printf ("val = %d\n" , ((struct Task*)pos)->val); } return 0 ; }
删除其实没啥看的,主要是看list_for_each_entry
函数
1 2 3 4 5 6 7 \#define list_for_each_entry(pos, head, member) \ for (pos = list_entry ((head)->next, typeof (*pos), member); \ &pos->member != (head); \ pos = list_entry (pos->member.next, typeof (*pos), member))
获取节点结构体的地址 1 2 \#define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
上一节31行展开就是:
1 2 3 4 5 6 for (task_pos = list_entry ((&task.entry_list)->next, typeof (*task_pos), entry_list); &task_pos->entry_list != (&task.entry_list); task_pos = list_entry (task_pos->entry_list.next, typeof (*task_pos), entry_list)) { xxxx }
核心还是用到了list_entry
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 struct Task *task_pos = ((type *)((char *)(ptr)-(unsigned long )(&((type *)0 )->member)))这里的参数 ptr, type, member分别是 ptr - (&task.entry_list)->next 这里的 ptr是找容器的那个变量的指针,这可以理解,第一次调用就是list中第一个entry_list *这个指针 (char *)ptr是为了计算字节偏移 type - typeof (*task_pos) ANSI C标准允许值为0 的常量被强制转换成任何一种类型的指针,并且转换的结果是个NULL ,因此((type *)0 )的结果就是一个类型为type *的NULL 指针. 如果利用这个NULL 指针来访问type的成员当然是非法的,但typeof ( ((type *)0 )->member )是想取该成员的类型,编译器不会生成访问type成员的代码 &( ((type *)0 )->member )在最前面有个取地址符&,它的意图是想取member的地址,所以编译器同样会优化为直接取地址。那我们可取到以“0 ”为基地址的一个type型变量member域的地址。那么这个地址也就等于member域到结构体基地址的偏移字节数。 (char *)(ptr)使得指针的加减操作步长为一字节,(unsigned long )(&((type *)0 )->member)等于ptr指向的member到该member所在结构体基地址的偏移字节数。二者一减便得出该结构体的地址。转换为 (type *)型的指针 member - entry_list ==> struct Task *task_pos = ((struct Task *)((char *)(给的entry_list)-(unsigned long )(&((struct Task *)0 )->entry_list)))