`
dsea
  • 浏览: 44543 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

linux内核链表以及list_entry

 
阅读更多

链表是一些包含数据的独立数据结构的集合,链表中的每一个节点通过链或者指针连接在一起。程序通过指针访问链表中的节点。链表一般分为单链表和双链表。

 

1.单链表

 

单链表中,每个节点包含指向下一个节点的指针。链表最有一个节点的指针字段值为NULL,表明链表后面不再有其它节点。下面是一张单链表的图:

 

对应的数据结构为:

 

typedef struct NODE
{
	int value;
	struct NODE *next;		
}Node;
 

2.双链表

 

在一个双链表中,每个节点都包含两个指针——指向前一个节点的指针和指向后一个节点的指针。这样的好处是我们可以从任何方向遍历双链表。

对应的节点数据类型为:

 

typedef struct NODE
{
	int value;
	struct NODE *fwd;		
	struct NODE *bwd;
}Node;
 

3.linux内核链表

 

2.6.31.13内核版本作为分析基础。不同版本之间的区别不大。链表结构定义为(节选自include/linux/list.h):

 

struct list_head {
	struct list_head *next, *prev;
};

  内核链表包含指向next和prev的指针,是一个双链表,不过不同于一般的双链表,内核链表不包含数据域,通常被用作双循环链表,当需要用到十字链表时,使用内核链表也很方便。

 

3.1 声明和初始化

 

linux内核提供了两种方式初始化链表。一种是使用LIST_HEAD()这个宏:

 

#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \
        struct list_head name = LIST_HEAD_INIT(name)

 

另外有一个内联函数用于运行时初始化:

static inline void INIT_LIST_HEAD(struct list_head *list)
{
	list->next = list;
	list->prev = list;
}
 

3.2 添加、删除

下面都是些很基本的操作,只要弄清楚了链表的原理,都很容易理解。

 

/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
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;
}

/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
        __list_add(new, head, head->next);
}

/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
        __list_add(new, 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);
        entry->next = LIST_POISON1;
        entry->prev = LIST_POISON2;
}

static inline void list_del_init(struct list_head *entry)
{
        __list_del(entry->prev, entry->next);
        INIT_LIST_HEAD(entry);
}

static inline void list_move(struct list_head *list, struct list_head *head)
{
        __list_del(list->prev, list->next);
        list_add(list, head);
}

static inline void list_move_tail(struct list_head *list,
                                  struct list_head *head)
{
        __list_del(list->prev, list->next);
        list_add_tail(list, head);
}

static inline int list_empty(const struct list_head *head)
{
        return head->next == head;
}

 

3.3 获取链表节点

 

linux链表中仅保存了list_head成员变量的地址,那么我们如何通过这个list_head的成员访问到它所有者节点的数据呢?linux提供了list_entry这个宏,ptr是指向该数据中list_head成员的指针,type是节点的类型,member是节点类型中list_head成员的变量名。

 

/**
* 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宏定义在include/linux/kernel.h

/**
* container_of - cast a member of a structure out to the containing structure
* @ptr:        the pointer to the member.
* @type:       the type of the container struct this is embedded in.
* @member:     the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({                      \
		const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
		(type *)( (char *)__mptr - offsetof(type,member) );})
 

offsetof在include/linux/stddef.h中

 

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

 

获得节点对象指针的原理图如下所示:

((type *)0)->member,它将0地址强制转换为type结构的指针,再访问到type结构中的member成员。offsetof取得list_head成员msg_node相对于结构体的偏移量。将指向当前节点对象member的地址减去偏移量,就可以得到节点地址,再将它转成指向节点结构类型的指针。

 

linux链表的基本操作已经完成了,其它如链表遍历的操作可查看list.h源码,有很详细的说明。

 

 

  • 大小: 17.2 KB
  • 大小: 21 KB
  • 大小: 12.1 KB
分享到:
评论

相关推荐

    深入浅出linux内核源代码之双向链表list_head pdf codes

    Linux内核提供了一系列的宏和函数来操作`list_head`,如`INIT_LIST_HEAD`用于初始化一个空链表,`list_entry`用于从链表节点获取实际数据结构的指针,`list_for_each`和`list_for_each_entry`用于遍历链表,`list_...

    Linux内核之list_head

    在Linux内核中,链表数据结构的实现主要集中在`include/linux/list.h`文件中。尽管本节以2.6内核为例,但2.4内核中的链表结构与其基本一致,仅有一些扩展,如链表的读拷贝更新(RCU)和HASH链表(hlist)等。 **基本...

    Linux内核链表移植和测试代码

    本知识点将深入探讨Linux内核链表的移植和测试,以及`list.c`和`list.h`这两个文件的作用。 Linux内核中的链表API主要集中在`<linux/list.h>`头文件中,但在提供的`list.h`文件中,我们可以推测它是对原生内核链表...

    详解linux内核链表之list.h

    本文将详述Linux内核中的链表实现,主要聚焦于`list.h`头文件中的相关概念、结构和函数,这对于理解和操作内核数据至关重要。 首先,`list.h`包含了Linux内核中双向链表的基本定义和操作。双向链表允许我们在列表的...

    深入浅出linux内核源代码之双向链表list_head.pdf

    ### 深入浅出 Linux 内核源代码之双向链表 `list_head` #### 一、双向链表概述 双向链表是一种常见的数据结构,在计算机科学领域有着广泛的应用。与单向链表相比,双向链表中的每个节点不仅包含指向下一个节点的...

    linux内核链表介绍与了解

    在Linux内核中,链表数据结构由`struct list_head`定义,包含两个指针`next`和`prev`,这使得内核链表具备双链表功能,形成一个双向循环链表。与传统链表不同的是,Linux内核中的链表并没有独立的数据域,而是将`...

    抽取linux内核链表模块

    标题中的"抽取linux内核链表模块"涉及到的是从Linux内核源码中提取出链表(list)的实现,以便在用户空间的Linux应用程序中复用这些功能。这一过程有助于理解链表的内部工作原理,并能在自己的代码中有效地利用内核...

    linux内核链表与测试代码

    本文将深入探讨Linux内核链表的特点、实现方式以及它与普通链表的区别,并通过测试代码来加深理解。 首先,我们要知道链表是一种线性数据结构,由一系列节点组成,每个节点包含数据和指向下一个节点的指针。在Linux...

    深入分析Linux内核链表

    ### 深入分析Linux内核链表 #### 一、引言 本文旨在深入剖析Linux内核中的链表结构及其实现原理。链表作为一种常见的数据结构,在Linux内核中有着广泛的应用,用于管理各种资源和服务。由于其灵活性和高效性,...

    Linux内核链表测试

    首先,我们需要了解Linux内核链表的基本结构和操作,然后讨论用户态移植的过程,最后,通过分析给定的文件`list.c`, `list.h`以及`student.h`来理解具体的实现。 **1. Linux内核链表基础** Linux内核链表是一个...

    【最强悍的链表】Linux 内核链表源码

    在实际编程中,为了使用Linux内核链表,我们需要在自定义的结构体中嵌入`list_head`,并根据需要调用上述API来操作链表。例如,在提供的`【最强悍的链表】Linux 内核链表源码.java`文件中,可能展示了如何在Java环境...

    Linux内核双向链表简单分析

    在Linux内核中,双向链表的实现主要依赖于`struct list_head`结构体。无论是链表头节点还是链表中的普通节点,都是`struct list_head`类型的实例。该结构体包含了两个指针字段:`next`和`prev`,分别指向当前节点的...

    linux内核链表测试用例

    下面我们将深入探讨Linux内核链表的基本概念、结构、操作以及测试用例的重要性。 **Linux内核链表基本概念** Linux内核链表(list_head)是内核中用于链接数据结构的标准机制。它们不同于传统的C语言数组或指针,...

    深入浅出linux内核源代码之双向链表list_head(list.h).pdf

    在Linux内核中,`list.h`头文件定义了双向链表的核心结构——`struct list_head`,这是一个非常简单的结构体: ```c struct list_head { struct list_head *next, *prev; }; ``` 这个结构体包含两个指针字段:`...

    linux内核链表(一套精彩的链表数据结构)

    Linux内核链表是操作系统核心中一种至关重要的数据结构,用于高效地组织和管理内存中的数据。在Linux内核源代码中,链表的实现主要集中在`include/linux/list.h`这个头文件中,提供了一套强大且灵活的链表操作接口。...

    用linux内核链表修改韦东山的MP3程序

    内核链表提供了丰富的操作函数,如`list_add()`、`list_del()`、`list_for_each_entry()`等,使得插入、删除和遍历操作非常方便且线程安全。 针对MP3播放程序,原始的链表可能是在用户空间实现的,而内核链表则在...

    Linux内核链表(移植完成)

    在Linux操作系统中,内核链表是一种非常基础且重要的数据结构,它被广泛用于实现各种内核级的数据管理,如进程管理、内存管理等。在本项目中,我们看到一个名为"Linux内核链表(移植完成)"的实现,这表明已经有人将...

    内核链表.pdf

    内核链表还提供了用于访问链表节点的宏,比如list_entry宏可以根据list_head结构的地址,计算出宿主数据结构的地址。这些宏在遍历链表和操作链表时非常有用。 在实际使用中,内核链表能够高效地对数据进行管理,...

    内核链表 -- 学生管理系统

    这可以通过内核链表提供的API来完成,如`list_add()`、`list_del()`、`list_for_each_entry()`等。 ```c // 添加学生到链表 void add_student(Student *new_student) { list_add_tail(&new_student->list, &...

    内核链表航班系统

    内核链表是Linux操作系统内核中的一种基础数据结构,用于高效地组织和管理内存中的数据元素。在“内核链表航班系统”这个项目中,我们利用内核链表来构建一个航班管理系统,该系统具备添加、删除以及查询航班等核心...

Global site tag (gtag.js) - Google Analytics