1. 前言
本文主要介绍了Linux内核中常用的一个宏:container_of。它的主要作用是根据一个结构体中某个成员的指针,推算出整个结构体的首地址。container_of的实现方法也会在文章中进行讲解。
2. container_of 宏的定义
container_of是Linux内核中一个非常重要的宏,其定义如下:
```
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
```
3. container_of 宏的作用
container_of主要用于在一个结构体中找到另一个结构体的位置。比如,我们有这样一个结构体:
```
struct teacher {
int id;
char name[32];
int age;
char *title;
};
```
现在我们有一个指向title的指针,如何找到对应的teacher结构体的地址呢?这个时候就可以使用container_of宏:
```
struct teacher *pteacher;
char *ptitle;
...
pteacher = container_of(ptitle, struct teacher, title);
```
以上代码会计算出ptitle所在teacher结构体的地址,并将结果存储在pteacher指针中。
4. container_of宏的实现
container_of宏的实现分为两个部分:首先定义了一个名为__mptr的指针,用于存储需要找到的成员的指针;然后使用offsetof宏计算出成员在结构体中的偏移量,并通过偏移量和__mptr计算出结构体的地址。
4.1 offsetof宏
在介绍container_of宏的实现之前,先来看一下其依赖的宏——offsetof。
offsetof宏用于计算结构体中某个成员的偏移量。其定义如下:
```
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
```
在该宏中,将一个类型为TYPE的0指针转换成TYPE *类型,然后取出该结构体中成员MEMBER的地址,再用指针地址的数值表示该地址的偏移量,就是该成员相对于结构体首地址的偏移量。
4.2 container_of 宏的实现
container_of宏的实现分为两个阶段。
第一阶段,定义一个__mptr,用于存储需要找的成员的指针。该成员的指针传入container_of宏的第一个参数中。
```
const typeof( ((type *)0)->member ) *__mptr = (ptr)
```
第二阶段,通过成员的偏移量计算出结构体的地址。这里使用了offsetof宏,计算出member成员在type结构体中的偏移量。
```
(type *)( (char *)__mptr - offsetof(type,member) )
```
将__mptr强制转换为char类型的指针,减去member在type中的偏移量,最终得到type结构体的首地址。将首地址转换为type类型的指针,就找到了需要的结构体。
5. 案例说明
5.1 用于处理链表的例子
在Linux内核中,container_of宏经常用于处理链表。链表中的节点结构体中一般都会包含一个成员,用于指向下一个节点。当需要遍历链表时,需要从每个节点中获得指向下一个节点的指针。这时就可以使用container_of宏来计算出节点的首地址。
例如,我们定义了一个链表节点结构体:
```
struct list_node {
int data;
struct list_node *next;
};
```
现在,我们定义了一个结构体变量p,其中包含一个链表节点的指针:
```
struct list_node *p;
```
若想获取p的地址,就可以使用container_of宏:
```
container_of(p, struct list_node, next);
```
以上代码会计算出p指向的节点的位置,并返回该节点所在结构体的首地址。
5.2 用于处理定时器的例子
定时器是一种非常常见的机制,会在到达某个特定时间之后执行预定的任务。在Linux内核中,定时器也可以使用container_of宏来处理。
例如,我们有以下代码:
```
struct timer_list {
...
void *data;
...
};
```
其中,timer_list结构体中的data成员指向的是用户定义的数据结构体。例如,我们可以定义一个以下结构体:
```
struct mydata {
int counter;
struct timer_list timer;
};
```
这个结构体包含一个计数器和一个timer_list结构体。
当需要处理定时器的回调函数时,就可以使用container_of宏来计算出包含timer_list结构体的mydata结构体的地址,进而获取数据信息。
```
void my_timer_callback(unsigned long data)
{
struct mydata *pdata = container_of(data, struct mydata, timer);
printk("counter = %d", pdata->counter);
}
```
在该代码中,通过传入的data参数获取timer_list结构体的地址,接着使用container_of宏计算出包含该结构体的mydata结构体的地址。最终,就可以读取mydata结构体中的计数器counter的值。
6. 总结
container_of是Linux内核中非常实用的一个宏,其作用在于帮助定位结构体中某个成员的地址,并找到包含该成员的完整结构体的地址。它的实现依赖于offsetof宏,它的主要作用是计算结构体中成员的偏移量。container_of的使用场景非常广泛,包括链表、定时器、内存分配管理等方面。对于熟练掌握Linux内核开发的开发者来说,掌握container_of宏的使用方法,对于提高代码的效率和可读性,都非常有帮助。 如果你喜欢我们三七知识分享网站的文章, 欢迎您分享或收藏知识分享网站文章 欢迎您到我们的网站逛逛喔!https://www.37seo.cn/
发表评论 取消回复