内核链表
# 内核数据结构之链表
在 Linux 内核中,链表的实现和运用独树一帜。
struct list_head {
struct list_head *next, *prev;
};
1
2
3
2
3
linux内核
采用这种双向链表的结构作为标准链表,以提供最大的灵活性。
我们将某种数据结构存放到链表中通常是在数据结构中嵌入一个链表指针,例如:
struct A {
int a;
int b;
struct A *prev;
struct A *next;
};
1
2
3
4
5
6
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
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
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
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