linux内核数据结构基础 -- 双向链表

本文如下部分:

1、内核中双向链表定义

2、常用方法列举 及原理分析

3、举例

目录

本文如下部分:

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; 
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值