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
三、关于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实例处在两条链表中。
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实例处在两条链表中。
相关文章推荐
- windows驱动开发学习笔记一双向链表LIST_ENTRY
- 关于linux的链表之list_entry和内存对齐
- list_entry()详解
- list_for_each_entry_continue()与list_for_each_entry_from()的区别
- 对WDK中对LIST_ENTRY的操作的相关函数的实现及简单运用
- fix W: Duplicate sources.list entry http://us.archive.ubuntu.com/ubuntu/ precise-updates/main amd64
- 由结构体成员地址计算结构体地址——list_entry()原理详解
- list_entry()宏
- list_for_each & list_entry & 对内核链表的理解
- #define list_entry(ptr, type, member) \ container_of(ptr, type, member)
- Linux内核中的list_for_each_entry
- 关于container_of和list_for_each_entry 及其相关函数的分析
- containof和list_entry的实现方式
- list_entry()宏
- 对WDK中LIST_ENTRY的遍历
- list_entry的宏定义
- 关于container_of和list_for_each_entry 及其相关函数的分析
- 比list_entry更简洁的写法list_for_each_entry
- list_entry()宏 .
- List of High Paying Entry Level Jobs