您的位置:首页 > 其它

EFI LIST_ENTRY

2013-05-22 16:59 162 查看
/article/7901407.html

EFI LIST

List是一个非常常见的数据结构,学过C的童鞋肯定都会非常熟悉这个东东,上学的时候老师都会教我们按照如下的方式定义一个双向List。
typedef struct _list_node {
struct _list_node * Flink;
struct _list_node * Blink;
uint32 data;
}list;
乍看这个list也没什么不好 中规中矩大家都是这么用的,可是我们来看一下EDK中的list的实现方式,就会发现还是有一丁点的差异。
typedef struct _EFI_LIST_ENTRY {
struct _EFI_LIST_ENTRY *Flink;
struct _EFI_LIST_ENTRY *Blink;
} EFI_LIST_ENTRY;
主要的差别在新的List结构中成员变量没有数据项 只有前驱和后继指针,这样一来这个链表就和特定的数据类型无关,也就是说可以用这个链表穿起任何类型的数据(包括自定义类型),EDK code中有类似定义的数据结构,其中Link作为该结构中的一个成员
typedef struct {
UINTN Signature;
EFI_LIST_ENTRY Link;
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
CHAR16 *OptionName;
UINTN OptionNumber;
UINT16 BootCurrent;
UINT32 Attribute;
CHAR16 *Description;
VOID *LoadOptions;
UINT32 LoadOptionsSize;
} BDS_COMMON_OPTION;
这样的结构串成链表以后就会如下图所示,当我们需要从链表中取出完整的结构时也只需要CR一下(你懂的,小学数学加减法)



Option = CR (Link, BDS_COMMON_OPTION, Link, BDS_LOAD_OPTION_SIGNATURE);
List比较常见的操作有插入、删除、交换等基本操作,这些操作也都非常简单,基本上就是连连看,比如在当前节点头部插入一个节点通常需要做的就是 该节点的前驱指向 头结点的前驱,头结点的前驱的后继指向该节点,该节点的后继指向头结点,头结点的前驱指向该节点(比较晕)。EFI中的list不是凭空发明出来的,linux中的list也是使用同样的定义方式,虽说只是比常规的list少了一个数据项,但是实质却是观念上的改变,它将数据与链表解耦了,使得所有的数据类型都都可以放在链表之中。

Peter
2010-12-16
————————————————————————————

http://www.biosren.com/thread-3440-1-1.html



UEFI小结-Handle的来龙去脉

三、关于EFI_LIST_ENTRY

要明白IHANDLE这个结构体,就要明白EFI_LIST_ENTRY是如何被使用的。EFI_LIST_ENTRY定义如下(EDK\Foundation\Library\Dxe\Include\LinkedList.h):

typedef struct _EFI_LIST_ENTRY {

struct _EFI_LIST_ENTRY *ForwardLink;

struct _EFI_LIST_ENTRY *BackLink;

} EFI_LIST_ENTRY;

大家立刻就会反应到,它用于实现双向链表。但是与一般的链表实现方式不一样,它纯粹是EFI_LIST_ENTRY这个成员的链接,而不用在乎这个成员所在的结构体。一般的链表要求结点之间的类型一致,而这种链表只要求结构体存在EFI_LIST_ENTRY这个成员就够了。比如说IHANDLE
*handle1,*handle2;初始化后, handle1->AllHandles->ForwardLink=handle2->AllHandles; handle2->AllHandles->BackLink=handle1->AllHandles。这样handle1与handle2的AllHandles就链接到了一起。但是这样就只能进行AllHandles的遍历了,怎么样遍历IHANLE实例呢?。这时候就要用到_CR宏,_CR宏的定义如下:

#define _CR(Record, TYPE, Field) ((TYPE *) ((CHAR8 *) (Record) - (CHAR8 *) &(((TYPE *) 0)->Field)))

这个宏可以通过结构体实例的成员访问到实例本身,它的原理可以参见

http://www.biosren.com/thread-1407-1-1.html或者http://blog.csdn.net/hgf1011/archive/2009/10/06/4635888.aspx

由handle1遍历到handle2的方法是这样的:IHANDLE
* handle=(IHANDLE*)_ CR(handle1 -> ForwardLink , IHANDLE , AllHandles )。

关于EFI_LIST_ENTRY就说的这里了。总结一点就是只要看到EFI_LIST_ENTRY,就应该联想到它的链表。像IHANDLE结构体中有两个EFI_LIST_ENTRY成员,就应该联想到每个IHANDLE实例处在两条链表中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: