本文如下部分:
1、内核中双向链表定义
2、常用方法列举 及原理分析
3、举例
目录
1、内核中双向链表定义
双向链表:
struct list_head {
struct list_head *next, *prev;
};
所在目录:include/linux/list.h
单纯的两个指针,描述前后项;
2、常用函数(宏函数)
-
双向链表定义和初始化
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
使用:
struct list_head global_list;
INIT_LIST_HEAD(&global_list);
-
添加元素
-
在传入节点之后的一个节点插入 (如果总是在最后一个节点插入,获取时从链表头开始就是,栈空间类型) static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } 传入节点的之前一个节点插入 (因为总是在之前一个节点插入,所以是队列的形式) static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } 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_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
static inline void __list_del(struct list_head *prev, struct list_head *next)
{
next->prev = prev;
prev->next = next;
}
static inline void list_del_init(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
INIT_LIST_HEAD(entry);
}
-
遍历链表
-
遍历链表 返回链表指针 #define list_for_each(pos, head) \ for (pos = (head)->next; prefetch(pos->next), pos != (head); \ pos = pos->next) 遍历链表返回链表指针 #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) 遍历链表所对应的结构,返回对应结构指针 #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ prefetch(pos->member.next), &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) 获取成员所属结构 #define list_entry(ptr, type, member) \ container_of(ptr, type, member) #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) 计算一个成员在结构中 距离结构首地址的偏移量 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
3、分析:
添加元素分析
list_add: 假设传入的第一个参数就是 new_b 第二个参数就是 B
那么分析下__list_add函数传入参数:
第一个参数就是 new_b 第二个参数就是 B->next 也就是C 第三个参数 B
static inline void __list_add(struct list_head *new_b,
struct list_head *C, struct list_head * B)
{
C->prev = new;
new->next = B;
new->prev = C;
B->next = new;
}
步骤1 2 3 4 如上图:
步骤1 时 原本C的prev 指向B ,执行后C的prev指向了new_b 所以,BC间变成了虚线在步骤1 中呈现;
list.h中包含了这些处理函数,我们稍加更改就可以拿到用户态测试:
我也是看到了其他人有用我测试了下确实可以加深理解:
下篇发 list.h修改后的源码:
测试程序如下:
根据打印可以看出 加入元素 list_add 和 list_add_tail分别表示 栈和队列两种不同的结构
#include <stdio.h>
#include <stdlib.h>
#include "my_list.h"
struct person
{
/* data */
int age;
int weight;
/* list */
struct list_head list;
};
int main(int argc, char* argv[])
{
struct person *tmp;
struct list_head *pos;
int age_i, weight_j;
struct person person_node;
INIT_LIST_HEAD( &(person_node.list) );
for(age_i = 10, weight_j = 35; age_i < 40; age_i += 5, weight_j += 5)
{
tmp =(struct person*)malloc(sizeof(struct person));
tmp->age = age_i;
tmp->weight = weight_j;
/* 头插 */
list_add( &(tmp->list), &(person_node.list) );
}
// 下面把这个链表中各个节点的值打印出来
printf("\n");
printf("=========== print the list ===============\n");
list_for_each( pos, &(person_node.list))
{
// 这里我们用list_entry来取得pos所在的结构的指针
tmp = list_entry(pos, struct person, list);
printf("age:%d, weight: %d \n", tmp->age, tmp->weight);
}
printf("\n");
// 下面删除一个节点中,age为20的节点
printf("========== print list after delete a node which age is 20 ==========\n");
struct list_head *del_tmp;
list_for_each_safe( pos, del_tmp, &(person_node.list) )
{
tmp = list_entry(pos, struct person, list);
if(tmp->age == 20)
{
list_del_init(pos);
free(tmp);
}
}
list_for_each( pos, &(person_node.list) )
{
tmp = list_entry(pos, struct person, list);
printf("age:%d, weight: %d \n", tmp->age, tmp->weight);
}
// 释放资源
list_for_each_safe( pos, del_tmp, &(person_node.list) )
{
tmp = list_entry(pos, struct person, list);
list_del_init(pos);
free(tmp);
}
return 0;
}