手机网站自适应布局怎么做,谷德设计网官网首页,购物网站图片的放大怎么做的,商标注册费用一般是多少钱理解Linux内核中的container_of宏 文章目录 理解Linux内核中的container_of宏1、了解C语言中的struct内存表示2、Linux内核的container_of宏实现理解3、Linux内核的container_of使用 Linux 内核包含一个名为 container_of 的非常有用的宏。本文介绍了解 Linux 内核中的 contain…理解Linux内核中的container_of宏 文章目录 理解Linux内核中的container_of宏1、了解C语言中的struct内存表示2、Linux内核的container_of宏实现理解3、Linux内核的container_of使用 Linux 内核包含一个名为 container_of 的非常有用的宏。本文介绍了解 Linux 内核中的 container_of 宏。本文包括一个简单的程序该程序说明了如何使用此宏并解释了为什么它如此有用。 1、了解C语言中的struct内存表示
C 中的结构是 C 编程语言的一个强大功能。它们允许您通过将不同的变量分组到一个名称下来创建复杂的数据类型。那么struct在内存中是如何表示的呢例如我们创建一个struct并访问它的成员地址。
struct Student {int age;float grade;char name[50];int weight;
}__attribute__((packed));请注意为了方便演示这里使用了结构成员对齐。关于C语言的结构成员对齐请参考C语言结构成员对齐、填充和数据打包 那么结构体Student在内存中的表示如下: 结构体的成员地址访问如下
#include stdlib.h
#include string.hstruct Student {int age;float grade;char name[50];int weight;
}__attribute__((packed));int main(int argc, char* argv[]) {struct Student st1;st1.age 17;st1.grade 7;st1.weight 120;memset(st1.name, 0, sizeof(st1.name));memcpy(st1.name, Jenson, 6);printf(sizeof struct %d\n, sizeof(struct Student));printf(st1s address %p\n, st1);printf(st1.age %p\n, st1.age);printf(st1.grade %p\n, st1.grade);printf(st1.name %p\n, st1.name[0]);printf(st1.weight %p\n, st1.weight);printf(-------------------\n);printf(ageaddr %#lx\n, ((unsigned long)st1));printf(gradeaddr %#lx\n, ((unsigned long)st1 sizeof(st1.age)));printf(nameaddr %#lx\n, ((unsigned long)st1 sizeof(st1.age) sizeof(st1.grade)));printf(weights addr %#lx\n, ((unsigned long)st1 sizeof(st1.age) sizeof(st1.grade) sizeof(st1.name)));return 0;
}我们将得到如下结果
sizeof struct 62
st1s address 0x7fe806bb28
st1.age 0x7fe806bb28
st1.grade 0x7fe806bb2c
st1.name 0x7fe806bb30
st1.weight 0x7fe806bb62
-------------------
ageaddr 0x7fe806bb28
gradeaddr 0x7fe806bb2c
nameaddr 0x7fe806bb30
weights addr 0x7fe806bb62如果结构体没有使用__attribute__((packed))则name成员与weight成员将有两个字节的差异因为编译器对结构体填充了两个字节从而达到内存对齐的目的。
2、Linux内核的container_of宏实现理解
在Linux内核中container_of宏的使用非常普遍其实现如下
#define offsetof(TYPE, MEMBER) ((size_t) ((TYPE*)0)-MEMBER)
#define container_of(ptr, type, member) ({ const typeof(((type *)0)-member)*__mptr (ptr); (type *)((char *)__mptr - offsetof(type, member)); }) container_of宏接受3个参数
ptr – 指向成员的指针。type – 嵌入其中的容器结构的类型。member – 结构中成员的名称。它返回成员的容器结构的地址。
为了方便理解我们将Linux内核的container_of宏应用到用户空间程序中示例代码如下
#include stdio.h#define offsetof(TYPE, MEMBER) ((size_t) ((TYPE *)0)-MEMBER)#define container_of(ptr, type, member) ({ \const typeof( ((type *)0)-member ) *__mptr (ptr); \(type *)( (char *)__mptr - offsetof(type,member) );})int main(void)
{struct sample {int mem1;char mem2;};struct sample sample1;printf(Address of Structure sample1 (Normal Method) %p\n, sample1);printf(Address of Structure sample1 (container_of Method) %p\n, container_of(sample1.mem2, struct sample, mem2));return 0;
}因为我们已经知道结构sample1 的地址那么程序输出的地址应该相同还是不同 让我们看看输出。
Address of Structure sample1 (Normal Method) 0x7feeaf2598
Address of Structure sample1 (container_of Method) 0x7feeaf2598可以知道示例程序输出的结果是相同的那么container_of宏是如何工作的呢让我们看看内核中的代码
/*** container_of - cast a member of a structure out to the containing structure* ptr: the pointer to the member.* type: the type of the container struct this is embedded in.* member: the name of the member within the struct.**/
#define container_of(ptr, type, member) ({ \const typeof( ((type *)0)-member ) *__mptr (ptr); \(type *)( (char *)__mptr - offsetof(type,member) );})下面我们将一步一步来解释这个宏是如何工作的首先让我们看第一行代码 const typeof( ((type *)0)-member ) *__mptr (ptr);对于const关键字在这里就不做解释了。在这行代码中我们对typeof感兴趣。
typeof()
typeof是非标准 GCC 扩展。GCC 扩展的类型允许我们引用表达式的类型并可用于声明变量。例如
int x 5;
typeof(x) y 6;
printf(%d %d\n, x, y);零指针引用
但是零指针取消引用呢获取成员的类型是一个小小的指针魔术。它不会崩溃因为表达式本身永远不会被计算。编译器关心的只是它的类型。如果我们要求回问地址也会发生同样的情况。编译器同样不关心值它只是将成员的偏移量添加到结构的地址中在本例中为 0并返回新地址。如下面代码所示
struct s {char m1;char m2;
};/* This will print 1 */
printf(%d\n, ((struct s*)0)-m2);#include stdio.h
int main(void)
{struct sample {int mem1;char mem2;};printf(Offset of the member %d\n, ((struct s*)0)-mem2);return 0;
}因此我们从
const typeof( ((type *)0)-member ) *__mptr (ptr)这行代码可以知道它创建了局部常量指针变量。正如示例代码所实现的那样
const typeof( ((type *)0)-member ) *__mptr (ptr)—– const char * __mptr sample1.mem2接下来让我们分析container_of宏的第二行代码 (type *)( (char *)__mptr - offsetof(type,member) )offsetof()宏
offsetof 是一个宏它将成员的字节偏移量返回到结构的开头。宏如下所示
#define offsetof(TYPE, MEMBER) ((size_t) ((TYPE *)0)-MEMBER)它甚至是标准库的一部分可在 stddef.h 中找到。
它返回一个名为 MEMBER 的成员的地址该成员的类型为 TYPE 的结构从地址 0恰好是我们正在寻找的偏移量存储在内存中。
现在我们需要根据我们的原始示例转换这个宏offsetofcontainer_of。这些宏将取代我们的示例如下所示。
container_of(sample1.mem2, struct sample, mem2)||||\/
const char * __mptr sample1.mem2;
struct sample * ((char*) __mptr - ((struct sample*)0)-mem2)||||\/
const char* __mptr 0x7FFD0D058784; //(Address of mem2 is 0x7FFD0D058784)
struct sample * (0x7FFD0D058784 - 4)||||\/
struct sample* (0x7FFD0D058780) //This is the address of the container structure3、Linux内核的container_of使用
container_of宏在Linux内核中的使用示伪代码例如下
struct foo {spinlock_t lock;struct workqueue_struct *wq;struct work_struct offload;(...)
};static void foo_work(struct work_struct *work)
{struct foo *foo container_of(work, struct foo, offload);(...)
}static irqreturn_t foo_handler(int irq, void *arg)
{struct foo *foo arg;queue_work(foo-wq, foo-offload);(...)
}static int foo_probe(...)
{struct foo *foo;foo-wq create_singlethread_workqueue(foo-wq);INIT_WORK(foo-offload, foo_work);(...)
}