linux内核中链表的实现
linux内核中链表的实现是相当的出色的,《linux内核设计与实现》(附录A)中说“linux内核使用了一种独一无二的方法遍历链表”、“为了这种巧妙设计,内核骇客们还是颇有点自豪的”。
-----------------------------------------------------------------------------------------------------------------------------------
1、链表结构体
struct list_head {
struct list_head *next, *prev;
};
很显然,这是一个双向链表。不过,令人疑惑的是这个链表结构中竟然没有存储节点数据的字段,这是因为
它不是单独使用的,而是需要加入到表示具体数据结构的结构体中,与表示具体数据结构的结构体组合一起
使用,才能显示出它的用途和价值。
《……设计与实现》上说“一个list_head结构体本身没有什么意义,通常需要把它嵌入到自己的结构
体中”。
例如 struct my_struct{
struct list_head list;
unsigned long dog;
void *cat;
}
这样表示具体数据结构的my_struct结构体就和list_head联系起来了。就可以把my_struct的对象通过
list成员链接起来,形成一个链表。
struct my_struct *p1,*p2,*p3......
p1->list->next=p2->list;
p2->list->pre=p1->list;
p2->list->next=p3->list;
p3->list->pre=p2->list;
p3->list->next=p1->list;
p1->list->pre=p3->list;
这样就通过list_head成员把my_struct的对象链接起来。
可以通过list_entry(p1->list,struct my_struct,list)来返回指向p1的my_struct指针,也就是
通过list_entry可以实现由list域访问其所属的结构体对象的功能。
2、链表操作函数
内核中很巧妙实现了链表的操作,大都非常简洁、精炼,通过宏或者内联函数实现,其中
list_entry的实现很有意思。
··初始化链表:
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = 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(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
new是新添加项,添加到结点head所在的链表中,并且是加到head之后第一个结点。
··删除操作(仅列出一个):
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;
}
从链表中删除entry,并使其指向安全的位置LIST_POISON1和LIST_POISON2,关于这两个常量的解释和定义在
linux/poison.h(22行)
/*
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
*/
#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA)
#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)
··遍历链表:
遍历链表list_for_each是一个宏,展开了就是一个for循环
#define list_for_each(pos, head) \
for (pos = (head)->next; prefetch(pos->next), pos != (head); \
pos = pos->next)
其中prefech(pos->next)是处理器预取pos->next,通过处理器的预取功能,对for循环进行了优化。
··访问数据操作:
#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)
container_of是一个有点复杂的宏,使用了C扩展关键字typeof,还有不好理解的求结构体成员
变量偏移.....
·((type *)0)是将0强制转化为type *,也就是指向type类型的指针
·((type *)0)->member是访问type结构中的member成员
·const typeof( ((type *)0)->member ) *__mptr 定义一个与((type *)0)->member同种类型的
指针变量(const变量)
·const typeof( ((type *)0)->member ) *__mptr=(ptr)对常量指针变量__mptr赋值
__mptr=ptr
·((size_t) &((TYPE *)0)->MEMBER得到member在type结构体中的偏移量,
offsetof(type,member)的展开效果
·(char *)__mptr - offsetof(type,member) 将__mptr强制转化为char类型指针,也就是__mptr
的地址,然后减去member在结构体中的偏移量,的到的自然就是结构体起始地址(偏移量求的很巧妙,
又很巧妙的获得结构体起始地址)。
·(type *)( (char *)__mptr - offsetof(type,member) )最后再强制转化为(type *)类型,
得到ptr所在的结构体对象(大功告成)。
到此成功的通过结构体对象的一个域的字段,获取了结构体对象本身,即访问list_head关联的结构对象
成功。
----------------------------------------------------------------------------------
取得结构体一个字段的偏移地址的方法 offsetof(type,member) 宏,设置的十分巧妙,
取得偏移后,具体对象的member地址-偏移地址,得到type对象的起始地址,设计相当的妙。
分享到:
相关推荐
在Linux内核中,双向链表的实现主要依赖于`struct list_head`结构体。无论是链表头节点还是链表中的普通节点,都是`struct list_head`类型的实例。该结构体包含了两个指针字段:`next`和`prev`,分别指向当前节点的...
linux内核链表的实现,包括内核链表的定义,以及内核链表相关的操作
将linux内核源码中list.h拿出来, 增删与遍历部分写了详细注释, 关于链表合并, 没用过所以没写. 源码版本是2.6.32, 不过链表的源码改动应该不是很大. 我的邮箱2253238252@qq.com, 代码有什么不对的欢迎发邮件给我
标题中的"抽取linux内核链表模块"涉及到的是从Linux内核源码中提取出链表(list)的实现,以便在用户空间的Linux应用程序中复用这些功能。这一过程有助于理解链表的内部工作原理,并能在自己的代码中有效地利用内核...
(1)内核链表中实现一个纯链表的封装,以及纯链表的各种操作函数 纯链表就是没有数据区域,只有前后向指针; 各种操作函数是节点创建、插入、删除、遍历。 这个纯链表本身自己没有任何用处,它的用法是给我们具体...
### Linux内核中链表和散列表的实现原理揭秘 #### 一、引言 Linux内核作为操作系统的核心部分,在其内部实现上广泛采用了多种高效的数据结构,如数组、链表和散列表等。其中,双向循环链表与散列表是内核中使用...
Linux内核链表是操作系统核心中一种重要的数据结构,它被广泛用于组织和管理系统中的各种数据,如设备列表、功能模块数据等。相比于数组,链表提供了更灵活的动态特性,无需预先知道数据总量即可创建,且能在任意...
本文将深入探讨Linux内核链表的实现,以帮助理解其内部工作原理。 Linux内核中的链表是通过定义一个名为`list_head`的结构体来实现的。这个结构体包含三个成员:`next`、`prev`和`__align Pad`。`next`和`prev`指针...
首先,我们需要了解Linux内核链表的基本结构和操作,然后讨论用户态移植的过程,最后,通过分析给定的文件`list.c`, `list.h`以及`student.h`来理解具体的实现。 **1. Linux内核链表基础** Linux内核链表是一个...
当多个键通过哈希函数映射到同一位置时,它们会在同一个链表中按照特定规则排列。 在用户态应用Linux内核哈希链表,你需要实现以下几个关键步骤: 1. **哈希函数设计**:根据数据的特性选择或设计合适的哈希函数,...
linux内核链表源代码,是list.h是链表的实现,有兴趣的朋友可以下载分析。
Linux内核中链表源码 list.h。是内核的/include/linux/list.h文件。
例如,在提供的`【最强悍的链表】Linux 内核链表源码.java`文件中,可能展示了如何在Java环境中模拟Linux内核链表的行为,虽然Java原生不支持C风格的链表,但可以通过对象引用和迭代器实现类似功能。 总之,Linux...
本知识点将深入探讨Linux内核链表的移植和测试,以及`list.c`和`list.h`这两个文件的作用。 Linux内核中的链表API主要集中在`<linux/list.h>`头文件中,但在提供的`list.h`文件中,我们可以推测它是对原生内核链表...
本文将深入探讨Linux内核链表的特点、实现方式以及它与普通链表的区别,并通过测试代码来加深理解。 首先,我们要知道链表是一种线性数据结构,由一系列节点组成,每个节点包含数据和指向下一个节点的指针。在Linux...
剖析linux 内核 linux内核链表
这个结构体应该还包含一个指针字段,用于链接到内核链表中的下一个学生节点。 ```c typedef struct student { char name[NAME_LEN]; int id; float score; struct list_head list; // 内核链表的节点 } Student...
"Linux内核链表及其在虚拟文件系统中的应用" Linux内核链表是一种抽象的双向循环链表结构,用于提高代码的重用性。这种链表可以将不同结构体类型的数据链接起来,并可以使 用相同的链表操作,从而能够有效地提高...
在这个项目中,“用Linux内核链表修改韦东山的MP3程序”,我们可以理解为将原有的MP3播放程序的链表实现替换为Linux内核级别的链表,以提高程序的性能和适应性,特别是对于基于友善之臂ARM9平台的嵌入式系统。...