Linux内核中的循环链表
-
List_head的数据结构就是一个双向链表的节点结构体定义,储存了pre指针和next指针,分别指向链表中前后元素。当pre或者next指针的值是NULL时,表示链表到了顶部或者结尾。在链表中一般就会有节点结构体变量,就是List_head这种,其中包含了pre,next还有所储存的数据data.
-
List_for_each 就是通过链表的pre和next指针进行便利。 由于该链表是一个循环链表,所以for循环只用从当前节点的下一节点开始一直遍历下去,直到指针指向当前节点表示绕完了一圈,遍历结束。
#define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next)
LIST_HEAD_INIT 为链表节点的初始化函数
static inline void INIT_LIST_HEAD(struct list_head *list) { //让节点 list 的前驱和后继都指向自己,构成了循环列表的初始结构 WRITE_ONCE(list->next, list); list->prev = list; }
List_add 也可以写成 list_insert 意为插入函数不过这里分为两种插入方法
头插法
//调用函数 static inline void list_add(struct list_head *new, struct list_head *head) { //传入的参数是新节点,当前节点,当前节点的后继 __list_add(new, head, head->next); } //new 为新建立的节点, prev为当前节点的位置,next是当前节点的后继 static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { //判断是否插入节点是否有效,就是是否搜索得到该节点位置 if (!__list_add_valid(new, prev, next)) return; next->prev = new; //让新节点成为当前节点后继的前驱 new->next = next; //让当前节点的后继变成新节点的后继 new->prev = prev; //让新节点的前驱成为当前节点 WRITE_ONCE(prev->next, new); } /* 例如 head -> head.next head.next->pre = new new->next = head.next new->pre = head 结果是 head -> nex > head.next 所以该插入法是一个头插法 */
尾插法
static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } //传入的参数改为了当前节点的前驱和当前节点 //这里往head的前面插入节点,由于这个链表是一个循环链表,所以往头节点前面插入等同于尾部插入 /* head->pre = new new->next = head new->pre = head->pre */
List_del 为删除节点的函数
//传入的参数是要删除节点的前驱和后继 static inline void __list_del(struct list_head * prev, struct list_head * next) { //让删除节点的后继的前驱指向自己的前驱 next->prev = prev; //让删除节点的前驱的后继指向自己的后继 prev->next= next); //总的来说就是让自己的前驱和后继互为前驱后继 } /* 例如 node 为删除节点 节点的前驱 node->pre = prenode 节点的后继 node->next= nextnode 初始链表 prenode -> node -> nextnode 然后进行操作 nextnode->pre = prenode prenode->next = nextnode 删除后的链表 prenode -> nextnode prenode <- node -> nextnode 可以看到node虽然指向着前后节点,当前后节点都和他没有了关系 最后释放掉node的空间就完全清除了该节点 */