ZhiBing's blog(码上看世界) ZhiBing's blog(码上看世界)
首页
  • Linux工具链

    • shell命令
  • 构建

    • CMake
    • Makefile
  • 版本管理

    • Git
    • Github
  • IDE及工具

    • vscode
    • CLion
  • 设计模式

    • 设计原则
  • 编程语言

    • C++
    • Go
    • Python
    • Shell
  • 调试

    • gdb
  • 开发者测试

    • gtest
  • 系统支撑

    • 操作系统
  • 性能优化

    • 编译优化选项
    • perf
    • valgrind
  • 容器

    • Docker
  • 微服务

    • Rancher
  • 其他
  • 随笔
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
关于
GitHub (opens new window)

ZhiBing Zheng

时间会回答成长
首页
  • Linux工具链

    • shell命令
  • 构建

    • CMake
    • Makefile
  • 版本管理

    • Git
    • Github
  • IDE及工具

    • vscode
    • CLion
  • 设计模式

    • 设计原则
  • 编程语言

    • C++
    • Go
    • Python
    • Shell
  • 调试

    • gdb
  • 开发者测试

    • gtest
  • 系统支撑

    • 操作系统
  • 性能优化

    • 编译优化选项
    • perf
    • valgrind
  • 容器

    • Docker
  • 微服务

    • Rancher
  • 其他
  • 随笔
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
关于
GitHub (opens new window)
  • 其他

    • 安装 mariadb 数据库
    • 创建自己的jekyll主题
    • 搭建私有云盘
    • 搭建Git服务器
    • Linux开放端口
    • 内核链表
    • 树莓派4安装k3d
    • 树莓派4安装k3s
    • 树莓派4更换镜像源
    • 树莓派4设置静态IP
    • 树莓派安装docker
    • 远程连接mariadb数据库
    • Latex
    • mac安装jekyll
    • docker + nextcloud 搭建私有云(Linux)
    • nginx使用自签名SSL证书
    • raspbian-buster-lite安装桌面
    • ubuntu18.04 + jekyll 搭建个人博客站
    • Ubuntu刷新DNS缓存
    • wsl后台运行程序
    • xshell script api
    • HPE gen10 plus 安装telsa P4驱动
    • HPE gen10 plus 安装ESXI 7
    • ESXI 7安装黑群晖
    • ESXI 7安装win10
  • 随笔

  • 专题

  • 友情链接
  • 更多
  • 其他
zhengzhibing
2022-06-16

内核链表

# 内核数据结构之链表

在 Linux 内核中,链表的实现和运用独树一帜。

struct list_head {
    struct list_head *next, *prev;
};
1
2
3

linux内核采用这种双向链表的结构作为标准链表,以提供最大的灵活性。

我们将某种数据结构存放到链表中通常是在数据结构中嵌入一个链表指针,例如:

struct A {
    int a;
    int b;
    struct A *prev;
    struct A *next;
};
1
2
3
4
5
6

但linux内核是将链表节点放入数据结构中:

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

struct A {
    int a;
    int b;
    struct list_head list;
};
1
2
3
4
5
6
7
8
9

随之而来的问题就是如何由节点访问到数据结构中的其他成员。linux内核提供了container_of()方法,让我们能够方便的从链表指针找到父结构对象。

// ptr: 父结构中的成员的指针(在链表中,就是节点指针)
// type: 父结构类型
// member: 成员(在链表中是节点)
#define container_of(ptr, type, member) ({                   \
	const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
	(type *)( (char *)__mptr - offsetof(type,member) );})
// 首先((type *)0)将0转换成type类型的指针,这个指针的数值为0;
// __mptr 获得成员指针;
// offsetof(type,member)获得成员到父结构的偏移;
// (type *)( (char *)__mptr - offsetof(type,member) ); 得到父结构指针;
1
2
3
4
5
6
7
8
9
10

以下面代码以及图示可以更好的理解。

#include <iostream>
#include <string>

using namespace std;

// ptr: 父结构中的成员的指针(在链表中,就是节点指针)
// type: 父结构类型
// member: 成员(在链表中是节点)
#define container_of(ptr, type, member) ({                   \
	const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
	(type *)( (char *)__mptr - offsetof(type,member) );})

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

struct Person {
    int age;
    bool sex;
    string name = "adfgdfgb";
    double height;
    list_head list;
};

int main(int argc, char** argv)
{
    Person p1 = {12, true, "Tom", 1.78};
    Person p2 = {19, false, "Jony", 1.66};
    p1.list.prev = nullptr;
    p1.list.next = &p2.list;
    p2.list.prev = &p1.list;
    p2.list.next = nullptr;

    if (container_of(p2.list.prev, Person, list) == &p1) {
        cout << "ok" << endl;
        cout << container_of(p2.list.prev, Person, list)->age << endl;
    }
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

结合代码,看图,实质上就是理解container_of是怎么求P1的地址的。其实就是? + offsetof = _mptr => ? = _mptr - offsetof

container_of在 C++中应用有一个严格的条件:结构中的成员是平凡类型或是标准布局类型,简单一点说就是,结构中的成员在编译期各个的相对位置就被ABI固定了,如果某个成员在结构中的大小可变,那么这个偏移就无法固定,则无法找到父结构。string虽然存放不定长的字符串,但是一个标准布局类型,字符串是分配在堆上,结构中存放的是它的地址。

上次更新: 2022/06/17, 07:22:19
Linux开放端口
树莓派4安装k3d

← Linux开放端口 树莓派4安装k3d→

最近更新
01
HPE gen10 plus 安装ESXI 7
06-12
02
ESXI 7安装黑群晖
06-12
03
ESXI 7安装win10
06-12
更多文章>
Theme by Vdoing | Copyright © 2022-2024 ZhBing Zheng | 粤ICP备2022062743号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式