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. 解决Cisco VPN Client:Reason 442: Failed to Enable Virtual Adapter VPN连接问题

    大公司里肯定涉及不同地点的办公问题,这样VPN的使用就频繁了,今天遇到一个VPN连接问题,分享给大家,看一眼,以后不在这问题上耗费太多功夫. 在win7上连接vpn时抛出“failed to enab ...

  2. CSS两列及三列自适应布局方法整理

    布局 自适应 两列 三列 在传统方法的基础上加入了Flex布局并阐述各方法的优缺点,希望对大家有所帮助.先上目录: 两列布局:左侧定宽,右侧自适应 方法一:利用float和负外边距 方法二:利用外边距 ...

  3. fill 函数

    fill函数的作用是:将一个区间的元素都赋予val值.函数参数:fill(first,last,val);//first为容器的首迭代器,last为容器的末迭代器,val为将要替换的值. 例题:给你n ...

  4. linux 使用文本编辑器编写shell脚本执行权限不够

    在linux下,自己编写的脚本需要执行的时候,需要加上执行的权限 解决方式:chmod 777 test.sh

  5. png24是支持Alpha透明的。。。。。。

    这个可能跟每个人使用切图软件有关. 1.Photoshop   1)只能导出布尔透明(全透明或者全不透明)的PNG8.    2)能导出alpha透明(全透明,全不透明,半透明)的PNG24.     ...

  6. JNDI学习总结(二)——Tomcat下使用C3P0配置JNDI数据源

    一.C3P0下载 C3P0下载地址:http://sourceforge.net/projects/c3p0/files/?source=navbar

  7. HDU1535——Invitation Cards(最短路径:SPAF算法+dijkstra算法)

    Invitation Cards DescriptionIn the age of television, not many people attend theater performances. A ...

  8. Django QuerySet API文档

    在查询时发生了什么(When QuerySets are evaluated) QuerySet 可以被构造,过滤,切片,做为参数传递,这些行为都不会对数据库进行操作.只要你查询的时候才真正的操作数据 ...

  9. 在安装ISE的情况下,充分利用ISE的安装目录,查找资料

    2013-06-22 11:03:02 在找资料时,通过官网输入关键字的方法找资料,有事会给出很多版本的链接.或者找不到,下面给出一种简便的方法,可以快速找到想要的资料. 如果要找ISE各个工具如pl ...

  10. ubuntu装机

    备份: .bashrc profile .vimrc exports defults/ 各种workspace中的源码 goagent/ 重转后安装: apt-get install openjdk- ...