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内核-双向链表的更多相关文章

  1. 例说Linux内核链表(三)

    经常使用的linux内核双向链表API介绍 linux link list结构图例如以下: 内核双向链表的在linux内核中的位置:/include/linux/list.h 使用双向链表的过程,主要 ...

  2. Linux 内核数据结构:Linux 双向链表

    Linux 内核提供一套双向链表的实现,你可以在 include/linux/list.h 中找到.我们以双向链表着手开始介绍 Linux 内核中的数据结构 ,因为这个是在 Linux 内核中使用最为 ...

  3. Linux 内核数据结构:双向链表

    Linux 内核提供一套双向链表的实现,你可以在 include/linux/list.h 中找到.我们以双向链表着手开始介绍 Linux 内核中的数据结构 ,因为这个是在 Linux 内核中使用最为 ...

  4. Linux内核中双向链表的经典实现

    概要 前面一章"介绍双向链表并给出了C/C++/Java三种实现",本章继续对双向链表进行探讨,介绍的内容是Linux内核中双向链表的经典实现和用法.其中,也会涉及到Linux内核 ...

  5. 深度剖析linux内核万能--双向链表,Hash链表模版

    我们都知道,链表是数据结构中用得最广泛的一种数据结构,对于数据结构,有顺序存储,数组就是一种.有链式存储,链表算一种.当然还有索引式的,散列式的,各种风格的说法,叫法层出不穷,但是万变不离其中,只要知 ...

  6. Linux 内核里的数据结构:双向链表

    原文:https://blog.csdn.net/qq_33487044/article/details/78827260 双向链表 Linux 内核自己实现了双向链表,可以在 include/lin ...

  7. Linux内核分析--内核中的数据结构双向链表【转】

    本文转自:http://blog.csdn.net/yusiguyuan/article/details/19840065 一.首先介绍内核中链表 内核中定义的链表是双向链表,在上篇文章--libev ...

  8. Linux内核中的双向链表struct list_head

    一.双向链表list_head Linux内核驱动开发会经常用到Linux内核中经典的双向链表list_head,以及它的拓展接口和宏定义:list_add.list_add_tail.list_de ...

  9. Linux内核分析--内核中的数据结构双向链表续【转】

    在解释完内核中的链表基本知识以后,下面解释链表的重要接口操作: 1. 声明和初始化 实际上Linux只定义了链表节点,并没有专门定义链表头,那么一个链表结构是如何建立起来的呢?让我们来看看LIST_H ...

随机推荐

  1. 【贪心】bzoj 3709:[PA2014]Bohater

    3709: [PA2014]Bohater Time Limit: 5 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 653  Solved:  ...

  2. POJ 1562 && ZOJ 1709 Oil Deposits(简单DFS)

    题目链接 题意 : 问一个m×n的矩形中,有多少个pocket,如果两块油田相连(上下左右或者对角连着也算),就算一个pocket . 思路 : 写好8个方向搜就可以了,每次找的时候可以先把那个点直接 ...

  3. POJ1265Area

    http://poj.org/problem?id=1265 题意 : 给你一个点阵,上边有很多点连成的多边形,让你求多边形内部的点和边界上的点以及多边形的面积,要注意他每次给出的点并不是点的横纵坐标 ...

  4. C++中的INL

    inl文件介绍 inl文件是内联函数的源文件.内联函数通常在C++头文件中实现,但是当C++头文件中内联函数过多的情况下,我们想使头文件看起来简洁点,能不能像普通函数那样将内联函数声明和函数定义放在头 ...

  5. MSSQLServer基础05(联合查询,连接查询)

    联合结果集union(集合运算符) 集合运算符是对两个集合操作的,两个集合必须具有相同的列数,列具有相同的数据类型(至少能隐式转换的),最终输出的集合的列名由第一个集合的列名来确定.(可以用来连接多个 ...

  6. 24点C++程序实现 编程之美1.16

    解法1,对于任意输入的四个数字,给出一个24点的解法,若无解,则没有输出. 原理参照下图(编程之美原书) 代码如下,仅供参考 // 1.16.cpp : Defines the entry point ...

  7. System.Drawing.Design.UITypeEditor自定义控件属性GetEditStyle(ITypeDescriptorContext context),EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.C ...

  8. [原]Unity3D深入浅出 - 粒子系统(Particle System)

    粒子系统是在三维空间渲染出来的二维图像,主要用于烟,火,水滴,落叶等效果.一个粒子系统由粒子发射器.粒子动画器和粒子渲染器三个独立的部分组成. Unity中自带了一些粒子效果,在Assets>I ...

  9. sencha touch tabsidebar 源码扩展

    先上图看效果 没错,这是一个sencha touch 项目,而这里的右边推出效果(下文叫做tabsiderbar),使用插件tabsiderbar来扩展的. 插件js下载地址:http://www.m ...

  10. CF GYM 100703L Many questions

    题意:题意真坑……龙要问一系列问题,王子骑士公主分别以一个整数回答,如果王子和公主答案差的绝对值比骑士和公主答案差的绝对值小则说王子和公主的答案更相似,反过来如果前者比后者大则说骑士和公主的答案更相似 ...