首页 > 代码库 > 开源GUI-Microwindows之程序入口分析
开源GUI-Microwindows之程序入口分析
***************************************************************************************************************************
作者:EasyWave 时间:2014.10.05
类别:开源GUI系统-Microwindows之程序入口分析 声明:转载,请保留链接
注意:如有错误,欢迎指正。这些是我学习的日志文章......
***************************************************************************************************************************
一:MicroWindows程序入口
这里只分析基于WIN32 Message方式部分,对于Nano不是这里的分析重点,相信熟悉Linux内核的,应该都知道在Linux下,不管是设备,驱动,还是进程,都是采用链表的方式将各个宿主数据结构链接起来,而在Microwindows中也采用内似的方式,我们先来复习下Linux的双向链表吧,在Linux内核中,有大量的数据结构需要用到双循环链表,例如进程、文件、模块、页面等。若采用双循环链表的传统实现方式,需要为这些数据结构维护各自的链表,并且为每个链表都要设计插入、删除等操作函数。因为用来维持链表的next和prev指针指向对应类型的对象,因此一种数据结构的链表操作函数不能用于操作其它数据结构的链表。
在Linux源代码树的include/linux/list.h文件中,采用了一种类型无关的双循环链表实现方式。其思想是将指针prev和next从具体的数据结构中提取出来构成一种通用的"双链表"数据结构list_head。如果需要构造某类对象的特定链表,则在其结构(被称为宿主数据结构)中定义一个类型为list_head类型的成员,通过这个成员将这类对象连接起来,形成所需链表,并通过通用链表函数对其进行操作。其优点是只需编写通用链表函数,即可构造和操作不同对象的链表,而无需为每类对象的每种列表编写专用函数,实现了代码的重用。如下所示的定义:
struct list_head {
struct list_head *next, *prev;
};
在Linux内核中的双循环链表实现方式如下:
- list_head类型的变量作为一个成员嵌入到宿主数据结构内;
- 可以将链表结构放在宿主结构内的任何地方;
- 可以为链表结构取任何名字;
- 宿主结构可以有多个链表结构;
- 用list_head中的成员和相对应的处理函数来对链表进行遍历;
- 如果想得到宿主结构的指针,使用list_entry可以算出来。
typedef struct dtest {
…
struct list_head list;
…
};
但是,如何通过list_head成员访问到宿主结构项呢?毕竟list_head不过是个连接件,而我们需要的是一个"特定"的数据结构链表。
先介绍几个基本宏:offsetof、typeof、containerof
#define __compiler_offsetof(a,b) __builtin_offsetof(a,b)
而__builtin_offsetof()宏就是在编译器中已经设计好了的函数,直接调用即可。
#undef offsetof //取消先前的任何定义,可以保证下面的定义生效
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
上面的算法一共分下面几步:
- ( (TYPE *)0 ) 0地址强制 "转换" 为 TYPE结构的指针;
- ((TYPE *)0)->MEMBER 访问结构中的数据成员;
- &( ( (TYPE *)0 )->MEMBER)取出数据成员的地址
- (size_t)(&(((TYPE*)0)->MEMBER))结果转换类型.
ontainer_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) );})
list_entry()宏,获取当前list_head链表节点所在的宿主结构项。第一个参数为当前list_head节点的指针,即指向宿主结构项的list_head成员。第二个参数是宿主数据结构的定义类型。第三个参数为宿主结构类型定义中list_head成员名。
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
扩展替换即为:
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
例如,我们要访问dtest链表(链表头为head)中首个元素,则如此调用:
list_entry(head->next, struct dtest, list);
经过C预处理的文字替换,这一行的内容就成为:
((struct dtest *)((char *)(head->next) - (unsigned long)(&((struct dtest *)0)->list)))
考虑list_head类型成员member相对于宿主结构(类型为type)起始地址的偏移量。对于所有该类型的宿主对象,这个偏移量是固定的。并且可以在假设宿主对象地址值为0,通过返回member成员的地址获得,即等于(unsigned long)(&((type *)0)->member)。这样,将当前宿主对象的"连接件"地址(ptr)减去这个偏移量,得到宿主对象地址,再将它转换为宿主数据结构类型的指针。 需要重申的是,链表头必须被嵌入到宿主对象中。
开源GUI-Microwindows之程序入口分析