container_of用法及实现

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/

点赞(72) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿
发表
评论
返回
顶部