linux内核-双向链表
linux中的经典宏定义
offsetof
定义:offsetof在linux内核的include/linux/stddef.h中定义。
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
说明:获得结构体(TYPE)的变量成员(MEMBER)在此结构体中的偏移量。
(01) ( (TYPE *)0 ) 将零转型为TYPE类型指针,即TYPE类型的指针的地址是0。
(02) ((TYPE *)0)->MEMBER 访问结构中的数据成员。
(03) &( ( (TYPE *)0 )->MEMBER ) 取出数据成员的地址。由于TYPE的地址是0,这里获取到的地址就是相对MEMBER在TYPE中的偏移。
(04) (size_t)(&(((TYPE*)0)->MEMBER)) 结果转换类型。对于32位系统而言,size_t是unsigned int类型;对于64位系统而言,size_t是unsigned long类型。
container_of
定义:container_of在linux内核的include/linux/kernel.h中定义。
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
说明:根据"结构体(type)变量"中的"域成员变量(member)的指针(ptr)"来获取指向整个结构体变量的指针。
(01) typeof( ( (type *)0)->member ) 取出member成员的变量类型。
(02) const typeof( ((type *)0)->member ) *__mptr = (ptr) 定义变量__mptr指针,并将ptr赋值给__mptr。经过这一步,__mptr为member数据类型的常量指针,其指向ptr所指向的地址。
(03) (char *)__mptr 将__mptr转换为字节型指针。
(04) offsetof(type,member)) 就是获取"member成员"在"结构体type"中的位置偏移。
(05) (char *)__mptr - offsetof(type,member)) 就是用来获取"结构体type"的指针的起始地址(为char *型指针)。
(06) (type *)( (char *)__mptr - offsetof(type,member) ) 就是将"char *类型的结构体type的指针"转换为"type *类型的结构体type的指针"。
Linux双向链表实现
Linux双向链表的定义主要涉及到两个文件:
include/linux/types.h
include/linux/list.h
(01). 节点定义
struct list_head {
struct list_head *next, *prev;
};
虽然名称list_head,但是它既是双向链表的表头,也代表双向链表的节点。
(02). 初始化节点
#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;
}
LIST_HEAD的作用是定义表头(节点):新建双向链表表头name,并设置name的前继节点和后继节点都是指向name本身。
LIST_HEAD_INIT的作用是初始化节点:设置name节点的前继节点和后继节点都是指向name本身。
INIT_LIST_HEAD和LIST_HEAD_INIT一样,是初始化节点:将list节点的前继节点和后继节点都是指向list本身。
(03). 添加节点
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_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);
}
__list_add(new, prev, next)的作用是添加节点:将new插入到prev和next之间。在linux中,以"__"开头的函数意味着是内核的内部接口,外部不应该调用该接口。
list_add(new, head)的作用是添加new节点:将new添加到head之后,是new称为head的后继节点。
list_add_tail(new, head)的作用是添加new节点:将new添加到head之前,即将new添加到双链表的末尾。
(04). 删除节点
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);
}
static inline void __list_del_entry(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
static inline void list_del_init(struct list_head *entry)
{
__list_del_entry(entry);
INIT_LIST_HEAD(entry);
}
__list_del(prev, next) 和__list_del_entry(entry)都是linux内核的内部接口。
__list_del(prev, next) 的作用是从双链表中删除prev和next之间的节点。
__list_del_entry(entry) 的作用是从双链表中删除entry节点。
list_del(entry) 和 list_del_init(entry)是linux内核的对外接口。
list_del(entry) 的作用是从双链表中删除entry节点。
list_del_init(entry) 的作用是从双链表中删除entry节点,并将entry节点的前继节点和后继节点都指向entry本身。
(05). 替换节点
static inline void list_replace(struct list_head *old,
struct list_head *new)
{
new->next = old->next;
new->next->prev = new;
new->prev = old->prev;
new->prev->next = new;
}

list_replace(old, new)的作用是用new节点替换old节点。
(06). 判断双链表是否为空
static inline int list_empty(const struct list_head *head)
{return head->next == head;
}
list_empty(head)的作用是判断双链表是否为空。它是通过区分"表头的后继节点"是不是"表头本身"来进行判断的。
(07). 获取节点
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
list_entry(ptr, type, member) 实际上是调用的container_of宏。
它的作用是:根据"结构体(type)变量"中的"域成员变量(member)的指针(ptr)"来获取指向整个结构体变量的指针。
(08). 遍历节点
#define list_for_each(pos, head) \
for (pos = (head)->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)
list_for_each(pos, head)和list_for_each_safe(pos, n, head)的作用都是遍历链表。但是它们的用途不一样!
list_for_each(pos, head)通常用于获取节点,而不能用到删除节点的场景。
list_for_each_safe(pos, n, head)通常删除节点的场景。
linux内核-双向链表的更多相关文章
- 例说Linux内核链表(三)
经常使用的linux内核双向链表API介绍 linux link list结构图例如以下: 内核双向链表的在linux内核中的位置:/include/linux/list.h 使用双向链表的过程,主要 ...
- Linux 内核数据结构:Linux 双向链表
Linux 内核提供一套双向链表的实现,你可以在 include/linux/list.h 中找到.我们以双向链表着手开始介绍 Linux 内核中的数据结构 ,因为这个是在 Linux 内核中使用最为 ...
- Linux 内核数据结构:双向链表
Linux 内核提供一套双向链表的实现,你可以在 include/linux/list.h 中找到.我们以双向链表着手开始介绍 Linux 内核中的数据结构 ,因为这个是在 Linux 内核中使用最为 ...
- Linux内核中双向链表的经典实现
概要 前面一章"介绍双向链表并给出了C/C++/Java三种实现",本章继续对双向链表进行探讨,介绍的内容是Linux内核中双向链表的经典实现和用法.其中,也会涉及到Linux内核 ...
- 深度剖析linux内核万能--双向链表,Hash链表模版
我们都知道,链表是数据结构中用得最广泛的一种数据结构,对于数据结构,有顺序存储,数组就是一种.有链式存储,链表算一种.当然还有索引式的,散列式的,各种风格的说法,叫法层出不穷,但是万变不离其中,只要知 ...
- Linux 内核里的数据结构:双向链表
原文:https://blog.csdn.net/qq_33487044/article/details/78827260 双向链表 Linux 内核自己实现了双向链表,可以在 include/lin ...
- Linux内核分析--内核中的数据结构双向链表【转】
本文转自:http://blog.csdn.net/yusiguyuan/article/details/19840065 一.首先介绍内核中链表 内核中定义的链表是双向链表,在上篇文章--libev ...
- Linux内核中的双向链表struct list_head
一.双向链表list_head Linux内核驱动开发会经常用到Linux内核中经典的双向链表list_head,以及它的拓展接口和宏定义:list_add.list_add_tail.list_de ...
- Linux内核分析--内核中的数据结构双向链表续【转】
在解释完内核中的链表基本知识以后,下面解释链表的重要接口操作: 1. 声明和初始化 实际上Linux只定义了链表节点,并没有专门定义链表头,那么一个链表结构是如何建立起来的呢?让我们来看看LIST_H ...
随机推荐
- SQL SERVER数据导入
我的博客已好久没有文字方面的记载了,好歹昨天已经结束软件设计师的考试了,今天怎么说也需要锻炼自己的写作能力.不然真怕自己又像上一年一样,一停就一年多了. 想好好学习数据库(SQL SERVER)方面的 ...
- BZOJ 4036 [HAOI2015] Set 解题报告
首先我们不能一位一位的考虑,为什么呢? 你想想,你如果一位一位地考虑的话,那么最后就只有 $n$ 个数字,然而他给了你 $2^n$ 个数字,怎么看都不对劲呀.(我是因为这样子弄没过样例才明白的) 所以 ...
- 解决VS如何同时打开两个工程(xp和win7)
http://www.360doc.com/content/11/1020/00/7891073_157586269.shtml
- struts2 标签的使用之二 s:iterator
struts2的s:iterator 可以遍历 数据栈里面的任何数组,集合等等 以下几个简单的demo:s:iterator 标签有3个属性: value:被迭代的集合 id :指定集 ...
- Android面试宝典(转)
Java知识点包括:接口与抽象的使用及区别,多线程,socket基础,集合类,也有个别公司考察定义,很无语. C/C++知识点包括:指针的移动,排序算法,链表,有时还会有二叉树的遍历或图的遍历. 1. ...
- [mock]12月11日
给出一个二叉搜索树的先序遍历,要求重新构造出这个二叉搜索树.一开始给出了一个递归的算法,指出复杂度有更优的解法.然后想出来一个O(n)的算法,就是使用一个栈,然后依次判断新的节点比原来的大还是小,然后 ...
- 跨平台查询文件时间,如果超过7天,删除该文件(windows和linxu测试过)
windows调用的是_stat函数,linux调用的是stat函数. #include <time.h> #include <sys/types.h> #include &l ...
- 分布式设计与开发(三)------高一致性服务ZooKeeper
分布式环境中大多数服务是允许部分失败,也允许数据不一致,但有些最基础的服务是需要高可靠性,高一致性的,这些服务是其他分布式服务运转的基础,比如naming service.分布式lock等,这些分布式 ...
- 使用QGridLayout布局实现翻页效果
http://blog.csdn.net/u013704336/article/details/51474942
- Python高级特性
比如构造一个1, 3, 5, 7, ..., 99的列表,可以通过循环实现: 这是正确的姿势