对比UEFI中_CR 和Linux中container_of宏定义

最近一段时间都是游走于Firmware和Linux之间,其中Firmware包括uboot、pmon、和UEFI。看到一些code的时候,当然会对两种架构做一些比较,这样可以加深理解和记忆。同时也许在debug的时候相互借鉴一下。

其实在UEFI中很多都借鉴了Linux架构的东西。窃以为,搭建和完善UEFI架构的家伙绝对也是Linux kernel的专家。

比如UEFI中有个宏定义_CR,其作用是依据一个结构体成员变量的内存地址,推算出该变量所在的结构体的基地址。看具体的code吧。

//
//  CONTAINING_RECORD - returns a pointer to the structure
//      from one of it's elements.
//
#define _CR(Record, TYPE, Field)  ((TYPE *) ((CHAR8 *) (Record) - (CHAR8 *) &(((TYPE *)0)->Field)))

其中0的利用是关键,以0为基础得到结构体变量在结构体中的偏移,成员变量的地址减去该偏移即可得到结构体的基地址,这是基本原理。


而Linux中其实也有一样的结构体container_of,个人觉得应该是UEFI借鉴Linux的思想。

/**
 * 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) );})

其中offsetof的地址如下:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

很明显,两个宏定义是差不多的。


另外,在网络上分别找到对应的两篇文章,从结构体地址上分析两个宏的原理,可以参考一下:

---------------------------------------------文章1---------------------------------------------------------

连接:http://blog.csdn.net/wbcuc/article/details/6003291


对于_CR这个macro,凡是搞UEFI的大家都会经常用到,个人也来谈谈它的具体实现,请各位高手指点。
//
//  CONTAINING_RECORD - returns a pointer to the structure
//      from one of it's elements.
//
#define _CR(Record, TYPE, Field)  ((TYPE *) ((CHAR8 *) (Record) - (CHAR8 *) &(((TYPE *) 0)->Field)))

 

BIOS技术网站,BIOS技术论坛,BIOS SPEC,BIOS手册,BIOS入门,ACPI,SMBIOS,BIOS技术,计算机研发,EC资料,EC开发,PS
这个宏的作用是根据一个结构体成员变量的的地址获得该结构体基地址。下面我们先来分析一个简单的例子:
typedef struct {

UINT16 a;

UINT16 b;

UINT32 c;

} PRIVATE_DATA;
我们知道了成员变量b的地址,如何得到PRIVATE_DATA这个Structure的地址呢?我们先来分解一下:
&(((TYPE *) 0)->Field),这里我们用b来取代Filed,0只是一个比较巧妙的构思,先不要在乎是否有意义。我们知道,结构体成员在内存中是由低地址向高地址存储的,这样,&(((TYPE *) 0)->b)实际上就是一个偏移量,这就是用0的巧妙之处。
(CHAR8 *) (Record)就是成员变量b的实际地址了,这里都需要做一个类型(CHAR8 *)的强制转换。
((CHAR8 *) (Record) - (CHAR8 *) &(((TYPE *) 0)->Field)),最后的结果意味着什么,想必是一目了然了。
然后把整个结果转换成所需要的类型指针,((TYPE *) ((CHAR8 *) (Record) - (CHAR8 *) &(((TYPE *) 0)->Field))),这样就大功告成了。
下面是图片,原理就是这样了。

 CR


---------------------------------------------文章2---------------------------------------------------------

原文地址:http://www.embedu.org/Column/Column433.htm


在linux 内核中有一个大名鼎鼎的宏container_of(),这个宏是用来干嘛的呢?我们先来看看它在内核中是怎样定义的。

呵呵,乍一看不知道是什么东东。

我们先来分析一下container_of(ptr,type,member),这里面有ptr,type,member分别代表指针、类型、成员。看一个例子:

Struct test
        {
                int i;
                int j;
                char k;
        };
        Struct test temp;

现在呢如果我想通过temp.j的地址找到temp的首地址就可以使用container_of(&temp.j,struct test,j);

现在我们知道container_of()的作用就是通过一个结构变量中一个成员的地址找到这个结构体变量的首地址。

下面来看看比较复杂的内容:

我们用上面的struct test张展一下

Const typeof(((struct test *)0)->j) * __mptr = (&temp.j);

其中,typeof是GNU C对标准C的扩展,它的作用是根据变量获取变量的类型。因此,上述代码的作用是首先使用typeof获取结构体成员j的类型为int,然后顶一个int指针类型的临时变量__mptr,并将结构体变量中的成员的地址赋给临时变量__mptr。

(struct test *)((char *)__mptr - offsetof(struct test,j));

接着我们来看一下offsetof(struct test,j),他在内核中如下定义

展开(size_t)&((struct test *)0)->j,这是什么东东?

一开始也不明白,这里要感谢曹忠明老师的热心帮助,一语惊醒梦中人,呵呵,可以是这样理解。

其中size_t是整型,那么我们可以知道最终的结果是一个整形值,也就是j相对于0地址的偏移量。也许现在你会问,整出这么个玩意干嘛,下面看个列子:

程序运行结果:

发现没有如果把第二个值 减去最后一个值,就能得到第一个值。

在回首一下它:

(struct test *)((char *)__mptr - offsetof(struct test,j));

是不是可以获得结构体变量temp的首地址呀,是不是太精妙了呀,linux内核中随随便便一个宏就有如此精妙,呵呵,想想对linux了解非常多的牛人,还有很长一段路。


  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值