{技术}{编程}关于 linux 内核分析之list_head 的请教

10 views
Skip to first unread message

Joe

unread,
Sep 17, 2009, 5:19:32 AM9/17/09
to pon...@googlegroups.com
对linux内核代码的一点疑惑:container_of的冗余?
 
    此宏代码出现在include/linux/kernel.h中,呃,这个是我在看linux中的通用链表结构时发现的,宏的作用是根据结构体中的链表连接指针得到相应节点的指针。(可以说这个设计相当的巧妙,不知道STL中的模板类是怎么实现通用链表的)。
现在的问题是,我就弄不明白,为什么要费尽周折的声明一个和ptr同类型的中间变量__mptr,然后只是简单的被cast,而实际上可以直接对ptr进行cast,效果是一样的。
我对修改后的代码做了一点测试,测试代码和结果如下:
(测试使用CYGWIN_NT-5.1, GCC3.4.4)

//    原本的container_of宏;
#define container_of(ptr, type, member) ({   \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})

//   去掉冗余代码的 container_of宏;
#define container_of2(ptr, type, member) ({   \
(type *)( (char *)ptr - offsetof(type,member) );})

typedef struct _st
{
int itemi;
char itemc[3];
struct _st * next;
}st;

int main (void){

st temp ={1,'a',0};
//    temp.next = &temp;

st *pst= container_of(&(temp.next), st, next);
st *pst2= container_of2(&(temp.next), st, next);

printf("&temp:\t\t%d\n", &temp);
printf("&(temp.itemi):\t%d\n", &(temp.itemi));
printf("&(temp.itemc):\t%d\n", &(temp.itemc));
printf("&(temp.next):\t%d\n", &(temp.next));
printf("container_of:\t%d\n", pst);
printf("pst->itemi:\t%d\n", pst->itemi);   // 验证指针的可用性
printf("container_of2:\t%d\n", pst2);
printf("pst2->itemi:\t%d\n", pst2->itemi);   // 验证指针的可用性
}

结果:


也许是我理解的不到位,谁能给我解释下这个为什么需要中间变量
__mptr?

Shuo Chen

unread,
Sep 17, 2009, 10:42:25 AM9/17/09
to TopLanguage
可能是防止误用吧。
比如原来的写法能检测出ptr指向的不是type.member的情况。ptr 如果类型不是 type::member*,那么原来的写法估计会报
错,比如container_of(&(temp.itemc), st, next);

STL好办,
struct node_base
{
node_base* next;
node_base* prev;
};

template<typename T>
struct node : public node_base
{
T value;
};

> <http://hiphotos.baidu.com/joec3/pic/item/c9a13b12cfa67fe5c2fd7853.jpg>
>
> 也许是我理解的不到位,谁能给我解释下这个为什么需要中间变量__mptr?

周龙亭

unread,
Sep 17, 2009, 11:41:37 AM9/17/09
to pon...@googlegroups.com
#define min(x,y) ({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x < _y ? _x : _y; })
和这个类似,为了确保 ptr 的类型是 type::member *

Dong Feng

unread,
Sep 17, 2009, 10:23:17 AM9/17/09
to pon...@googlegroups.com

那个看似没有用的赋值没有用强转。所以如果你coding的时候用了不匹配的ptr和type,compile的时候就会报错。

这是为了错误处理。


2009/9/17 Joe <qiaoc...@gmail.com>

Raven Dark

unread,
Sep 17, 2009, 10:23:12 AM9/17/09
to pon...@googlegroups.com
安全...?

2009/9/17 Joe <qiaoc...@gmail.com>

jingmi

unread,
Sep 17, 2009, 3:03:55 PM9/17/09
to TopLanguage
你说的这个例子,仅仅是为了避免多余的 side effect 而不是类型转换,比如 min(a++, ++b) 导致的 a、b 多次自增。

康神的一段类似的代码被不明真相的小盆友"优化"掉了这段赋值,发生过严重的问题。

Joe 提到的这个宏,除了类型检查,主要也是为了避免这个问题,比如

container_of(find_the_ptr_with_side_effect(my_ptr), type_name,
member_name);

如果的 find_the_ptr_with_side_effect() 存在 side effect 的话,就可能导致问题。

Shuo Chen

unread,
Sep 17, 2009, 9:12:37 PM9/17/09
to TopLanguage
两种写法 ptr 都只展开一次?如何会导致问题?

周龙亭

unread,
Sep 17, 2009, 9:19:33 PM9/17/09
to pon...@googlegroups.com
container_of宏应该没有避免 side effect的作用的,因为ptr只被展开了一次。
min 除了能避免side effect,也是有 type-checking 作用的,
(void) (&_x == &_y); 这个冗余的指针比较,就是做 strict type-checking。

2009/9/18 jingmi <jin...@gmail.com>

jingmi

unread,
Sep 17, 2009, 10:48:26 PM9/17/09
to TopLanguage
我看错了。。。container_of2() 在这里的确也不会引起啥问题

半夜睡之前发的,真没注意,因为习惯性的我都会这么写。

min 的 (void) (&_x == &_y) 这个也的确是为了做类型检查,不过如果不是为了避免被多次展开,直接用 x 和 y 也是可以
的。

Joe

unread,
Sep 17, 2009, 10:48:47 PM9/17/09
to pon...@googlegroups.com
见识了,之前没有想到类型检查,“那个看似没有用的赋值没有用强转”
谢谢各大侠的指点。

以前只是觉得不要在宏调用里面使用指针运算的参数(防止多次展开出问题),这下有了新的处理方法了。

2009/9/18 周龙亭 <njdra...@gmail.com>
Reply all
Reply to author
Forward
0 new messages