您的位置:首页 > 其它

RTEMS 4.9.4 中的对象分析

2011-03-11 23:37 85 查看
本文系原创,转载请注明出处,谢谢。





Object_Information是对象信息表的定义,该定义如下:

typedef struct {/*对象信息表的定义*/

Objects_APIs the_api; /*该对象的类型*/

uint16_t the_class; /*该对象集合属于什么样的类型*/

Objects_Id minimum_id; /*该对象类型最小的合法id值*/

Objects_Id maximum_id; /*该对象类型最大的合法id值*/

Objects_Maximum maximum; /*该对象类型可容纳的对象数目*/

bool auto_extend;/*为真表示不限制该对象的数量*/

uint32_t allocation_size;/*每个块中对象实例数量*/

size_t size; /*每个对象实例占用字节数*/

Objects_Control **local_table;/*指向本地对象的表 */

Chain_Control Inactive; /*这个是一个不活动控制块链表*/

Objects_Maximum inactive; /*在不活动链表中的实例数量*/

/*每个块中不活动的对象数量*/

uint32_t *inactive_per_block;

void **object_blocks; /*不活动的对象内存块链表*/

bool is_string; /*如果是真表示名字是字符串*/

uint16_t name_length;/*表示名字的最大长度*/

Objects_Thread_queue_Extract_callout extract;

#if defined(RTEMS_MULTIPROCESSING)

Chain_Control *global_table;

#endif

} Objects_Information;

typedef struct {/*实例控制块的定义*/

Chain_Node Node; /*双向链表的节点*/

Objects_Id id; /*实例的实例识别编号*/

Objects_Name name; /*实例的名称*/

} Objects_Control;

变量the_api表示此对象信息表管理的对象隶属于何种接口。RTEMS将接口(API)分为以下5类:

OBJECTS_NO_API,无接口类型,用于接口信息的管理;

OBJECTS_INTERNAL_API,RTEMS内核使用的接口;

OBJECTS_CLASSIC_API,RTEMS内核提供给应用程序的接口;

OBJECTS_POSIX_API,RTEMS向应用程序提供的POSIX接口;

OBJECTS_ITRON_API,RTEMS向应用程序提供的ITRON接口。

每种接口都提供了丰富的调用,系统将这些调用按功能进行划分,每个划分的调用集合使用OO思想,以类的方式体现。

使得内核架构清晰简洁,代码的扩展性和复用性大大增强。如果the_api表示对象属于RTEMS内核使用的接口,

那么the_class可以是以下3个类之一:

OBJECTS_INTERNAL_NO_CLASS,无类型对象,用于对象管理;

OBJECTS_INTERNAL_THREADS,内部任务对象,管理内核使用的任务;

OBJECTS_INTERNAL_MUTEXES,互斥体对象,用于内核临界资源的管理。

如果the_api表示对象属于内核提供给应用程序的接口,

那么the_class可以是以下11个类之一:

OBJECTS_CLASSIC_NO_CLASS,无类型对象,用于对象管理;

OBJECTS_RTEMS_TASKS,任务对象,提供任务相关的操作和管理;%,如任务的创建、删除、暂停、重启等;

OBJECTS_RTEMS_TIMERS,时间对象,提供与时间相关的操作和管理;%,如定时器的创建、删除等;

OBJECTS_RTEMS_SEMAPHORES,信号量对象,提供信号量相关的操作和管理;%,如信号量的创建、获取、释放等;

OBJECTS_RTEMS_MESSAGE_QUEUE,消息队列对象,提供消息队列相关的操作和管理;%,如消息队列的创建、消息的接收、发送等;

OBJECTS_RTEMS_PARTITIONS,固定内存对象,提供固定内存的管理与分配;%,如内存池的建立、内存的申请、释放等;

OBJECTS_RTEMS_REGIONS,变长内存对象,提供变长内存的管理与分配;%,如变长内存池的建立、申请、释放等;

OBJECTS_RTEMS_PORTS,双口内存对象,提供双口内存的地址转换;%,如双口内存的建立、地址转换等;

OBJECTS_RTEMS_PERIODS,调度时间对象,提供对调度时间的操作和管理;%,如调度器的建立、时间的统计、取消等;

OBJECTS_RTEMS_EXTENSIONS,扩展对象,对RTEMS用户扩展进行管理;%,如扩展接口的建立、删除等;

OBJECTS_RTEMS_BARRIERS,屏障对象,提供屏障的操作和管理。%,如屏障的建立、释放等。

如果the_api表示对象属于POSIX提供的接口,

那么the_class可以是以下13个类之一:

OBJECTS_POSIX_NO_CLASS,无类型对象,用于对象管理;

OBJECTS_POSIX_THREADS,线程对象,提供对线程相关的操作和管理,与应用程序接口的任务对象类似;

OBJECTS_POSIX_KEYS,线程私有数据(Thread-specific Data,或TSD)对象,在多线程环境下,

提供线程私有的全局变量的管理,这些全局变量仅在某个线程中有效,但却可以跨多个函数访问;

OBJECTS_POSIX_INTERRUPTS,

OBJECTS_POSIX_MESSAGE_QUEUE_FDS,

OBJECTS_POSIX_MESSAGE_QUEUES,

OBJECTS_POSIX_MUTEXES ,

OBJECTS_POSIX_SEMAPHORES,

OBJECTS_POSIX_CONDITION_VARIABLES,

OBJECTS_POSIX_TIMERS,

OBJECTS_POSIX_BARRIERS,

OBJECTS_POSIX_SPINLOCKS,

OBJECTS_POSIX_RWLOCKS,

如果the_api表示对象属于ITRON提供的接口,

那么the_class可以是以下9个类之一:

OBJECTS_ITRON_NO_CLASS,

OBJECTS_ITRON_TASKS,

OBJECTS_ITRON_EVENTFLAGS,

OBJECTS_ITRON_MAILBOXES,

OBJECTS_ITRON_MESSAGE_BUFFERS,

OBJECTS_ITRON_PORTS,

OBJECTS_ITRON_SEMAPHORES,

OBJECTS_ITRON_VARIABLE_MEMORY_POOLS,

OBJECTS_ITRON_FIXED_MEMORY_POOLS,

以上所有的类型可以看成从一个基类派生下来的类,RTEMS的基类提供了最基本的创建、删除、扩展等管理函数。这些派生类

基本上都是利用基类提供的方法完成对实例的管理。

Objects_Id类型用于定义实例识别编号,在RTEMS中是一个32Bit整数,如果一些系统有资源上的限制也

可以使用16Bit的整数表示,但这样RTEMS就无法支持多处理器模式。如图/ref{objectid}所示,32Bit的识别编

号有4部分组成,比特位27到31存储类(class)类型,可以表示31种不同的类类型,比特位24到26存储接口(API)类型,

可以表示7种不同的接口类型,比特位16到23存储节点编号(node),可以表示255个不同的节点,比特位0到15存储对象

实例编号(index),可以表示65535个对象实例;16Bit的编号由3部分组成,比特位11到15存储类(class)类型,

可以表示31种不同的类类型,比特位8到10存储接口(API)类型,可以表示7种不同的接口类型,比特位0到7存

储对象实例的编号(index),可以表示254个对象实例。32Bit与16Bit的实例识别编号,除了表示的对象实例数量不一样外,

就是16Bit的数据无节点编号,RTEMS是支持多处理器的,它将每一个处理器抽象为节点并赋予一个节点编号。程序中只要得到

一个对象的识别编号,就可知道这个对象是隶属于什么接口、什么类型、存在于哪个节点上。

/caption{32Bit和16Bit对象实例识别号(Objects/_Id)}



%RTEMS允许不限制对象的实例数量。对于这样的对象,其对象编号的最高为为1。

minimum_id和maximum_id分别存储对象信息表中管理的最小和最大的实例识别编号。maximum表示信息表中管理的

对象数量。auto_extend如果它的值为TRUE,表示不限制对象实例的数量。但这并不意味着可以有无限多个对象实例,32Bit的

实例识别编号限制同一对象的实例数不超过65535个,若使用16Bit的实例识别编号,则限制不超过254个。所谓不限制对象实例数量,是不受

maximum指定数量的约束,如果auto_extend为FALSE,那么对象实例数量是不会超过maximum指定的数量。

allocation_size表示一个块(block)内的对象实例数量。size表示一个对象实例占用的内存尺寸。这两个变量的乘积即是一个块占用内存的尺寸。

块是对象信息表管理的最小内存单位。当系统初始化对象信息表时,最少要申请一个块的内存用于实例的申请;当允许对象信息表字段自动扩展时,也是按照一个块的尺寸

增大的。如果希望每次增长的内存尺寸正好是一个对象实例的尺寸,初始化时,只需将allocation_size设置成1即可。

对象信息表管理着四个表和一个不活动对象链表。如图/ref{objinfotbl}所示,local_table局部实例表,它是一个Object_Control指针类型的数组,

用于保存本处理器中该对象已被申请的实例的指针,一个元素即是一个实例的指针,一共有maximum + 1个元素;

global_table是全局实例表,它是一个链表控制块数组(Chain_Control类型),共有节点数(多处理器系统的处理器数量)加1个元素,它按节点存储着

属于本对象的全局实例,每一个节点是一个链表,全局对象会按节点插入到相应的链表中;

inactive_per_block是一个整数数组,有多少个块就有多少个数组元素,每个元素表示对应的块中未被申请的实例数量;object_block是一个无类型

指针(void *)数组,有多少个块就有多少个数组元素,每个元素指向尺寸为allocation_size * size的内存块首地址。注意,只有局部实例表的数组比实际的用量

多1个元素,由于实例识别编号是从1开始,而C/C++语言的数组都是从0开始,为了访问方便,数组的首元素废弃不用。

Inactive是对象信息表中全部的未被申请实例的链表,inactive是对象信息表中全部未申请实例的数量。

对于不支持扩展的对象信息表,这个数量和inactive_per_block中对应块的实例数量都不会随着实例的申请释放产生变化;

支持扩展的信息表,会真实的反应信息表中未申请实例的数量。

对象信息表内部数组



以操作系统内部互斥体(Mutex)对象为例,它数据类型的定义如下:

typedef struct {/*Mutex 对象控制块定义*/

Objects_Control Object;/*Mutex 实例的控制块*/

CORE_mutex_Control Mutex; /*Mutex 实例的数据域*/

} API_Mutex_Control;

那么Mutex的对象信息表内size等于API_Mutex_Control类型所占用的字节数。一个块中有可能不止一个实例,有几个实例块就被均分成几个小内存,

然后系统使用双向链表将这些小内存串起来。API_Mutex_Control类型第一个数据成员是实例控制块类型,而实例控制块的第一个数据成员是双向链表的节点。无论

这个小内存块被解释成双向链表的节点也好,或解释成互斥体对象也好,都不影响系统对链表的操作。这里可以看成互斥体对象控制块继承了双向链表的节点,

这是C语言表达OO思想的一种方法。

is_string表示实例的名称是否是为字符串,如果其值为真,名称为字符串;如果为假,名称为一个4字节的无符号整数。name_length表示名称的最大长度,该

长度会以4向上对齐。注意,实际使用中,可以设置超过系统限制的最大字符串长度,只是系统保存的名称最长不超过name_length指定的长度。例如,系统指定的名称为8个

字符长,那么名称"object_a1"和"object_a2"系统是区分不开的,在使用实例名称时应特别注意。

extract是一个函数指针。当任务就绪从任务队列中解链时,如果是多处理器系统,任务需要告知该任务等待的实例,该对象已经由阻塞态进入就绪态。由于等待的实例和任务

有可能不在同一个节点上,需通过MPCI层通讯以同步所有处理器上该实例的状态。(是不是做这个工作???)可见,当系统中只有一个处理器时,系统是不会调用这个函数的。

全局实例表的工作与局部实例表的处理方式有所不同。RTEMS的全局实例控制结构定义如下:

typedef struct {/*全局实例控制块定义*/

Objects_Control Object;/*实例的控制块*/

uint32_t name; /*实例的名称*/

} Objects_MP_Control;

全局实例对于整个系统的节点都是可见的,同时对于这个全局实例所处的节点来说,它也是一个局部实例。所以系统没有为全局对象提供任何

的数据存储空间,只是把局部实例的对象识别号和名称保存在数据结构中。操作全局实例,都会把操作映射到它所在的局部实例。通过实例识别编号

可以获取实例所在的节点、接口和类型,很方便的定位实例并映射操作。RTEMS定义了全局空闲实例控制块链表_Objects_MP_Inactive_global_objects,

它用于保存待申请的全局实例控制块。系统启动后会按照配置要求,初始化一定数量的全局实例控制块保存在这个双向链表中。当某个实例在某个节点被创建时

指明为全局实例,完成本地实例的创建后,系统会从_Objects_MP_Inactive_global_objects中申请一个全局实例控制块,将局部实例的

实例识别编号和名称保存在全局实例控块中,然后将其插入相应对象信息表内的全局实例表中,同时通知其他节点,该实例已经被成功创建。

释放一个实例时,除了本地要做的工作外,还需要判断它是不是全局实例,如果是全局实例,还需要从全局实例表中找到该实例的控制块,

将其解链插入到_Objects_MP_Inactive_global_objects链表中,无论释放的实例是否在本地,都需要通知系统中其他节点,该实例已被释放。

/subsection{操作}

/subsubsection{概述}

RTEMS为对象信息表的使用和管理提供了丰富的操作,这些操作分列如下:

_Objects_Initialize_information,初始化一个指定类型的对象信息表;

_Objects_API_maximum_class,返回指定接口类型中具有的对象类型数量;

_Objects_Extend_information,对指定的对象信息表进行扩展,以容许提供更多的实例被使用;

_Objects_Shrink_information,紧缩对象信息表管理的块占用的内存,一个块内的实例全部未被使用则它占用的内存将被释放;

_Objects_Allocate,从对象信息表管理的不活动实例链表中申请一个实例;

_Objects_Allocate_by_index,指定需申请的实例序号,从对象信息表管理的实例里申请具有该序号的实例;

_Objects_Open,将实例指针按序号赋值到指定的对象信息表内的局部实例表中,并安装实例的名称;

_Objects_Close,从局部实例表中移除该实例,并清空实例的名称。若名称是整数,则清0;若名称是字符串,释放字符串所占用的内存并清空指针;

_Objects_Free,将指定的实例释放到对象信息表内的未被申请实例链表中;

_Objects_Name_to_id_u32,给定实例的名称,实例的名称是4字节的无符号整数,对指定的对象信息表搜索,如果发现名称相符的实例,返回其实例识别编号(还可以指定节点编号);

_Objects_Name_to_id_string,给定实例的名称,实例的名称是字符串,对指定的对象信息表搜索,如果发现名称相符的实例,返回其实例识别编号;

_Objects_Id_to_name,返回给定实例识别编号所对应的实例名称;

_Objects_Set_name,设置对象实例的名称;

_Objects_Get_name_as_string,实例识别编号指定的实例名称按字符串返回;

_Objects_Namespace_remove,清空实例名称占用的内存,如果是字符串,则释放占用的内存,并将指针清空;如果是整数,将名字清0;

_Objects_Get_information,给定接口类型和对象类型获取对象信息表的指针;

_Objects_Get_information_id,按照实例的实例识别编号返回对象信息表指针;

_Objects_Get,给定实例的实例识别编号从给定的对像信息表内的局部实例表中返回该实例的指针,若未成功,在全局实例表中再试一次;

_Objects_Get_isr_disable,从对象信息管理表内按照给定的实例识别编号从局部实例表中获取该实例,如果未成功,从全局实例表中再试一次,本方法采用关闭中断(全局临界区)的方式保护起来;

_Objects_Get_no_protection,从对象信息管理表内按照给定的实例识别编号从局部实例表中获取该实例,这个方法未使用临界区保护;

_Objects_Get_by_index,给定实例的序号从给定的对象信息表内的局部实例表中返回该实例的指针;

_Objects_Get_next,从给定的对象实例编号开始(包括该编号),在给定的对象信息表内的局部实例表向后寻找第一个可用的实例,返回其实例识别编号和指针。

多处理器环境下,系统还提供了如下的操作:

_Objects_MP_Allocate_global_object,申请全局实例控制块并将其返回给调用者,从链表_Objects_MP_Inactive_global_objects中获取一个节点并将其返回;

_Objects_MP_Free_global_object,释放全局控制块,将给定的全局实例控制块插入到链表_Objects_MP_Inactive_global_objects中;

_Objects_MP_Handler_initialization,按给定的全局控制块数量生成足够的全局控制块,初始化链表_Objects_MP_Inactive_global_objects,并将

全部控制块插入全局链表中;

_Objects_MP_Open,将全局实例按给定的信息插入到合适的对象信息表内的全局实例表中;

_Objects_MP_Allocate_and_open,申请一个全局实例控制块,并按给定的信息将其插入到合适的对象信息表内的全局实例表中;

_Objects_MP_Close, 从指定的全局对象实例表中释放指定的全局实例;

_Objects_MP_Global_name_search,对指定的处理器节点,搜索指定的对象实例表,用名称寻找指定的实例,发现后返回实例的实例识别编号;

_Objects_MP_Is_remote,从指定的对象信息管理表内的全局实例表中,按指定的实例识别编号返回实例的指针;

提供的操作较多,但从功能上只分为信息表的管理和实例的管理两类。信息表的管理主要是信息表的扩展操作,信息表的初始化也是通过扩展操作完成的。

实例的管理主要是申请、释放以及实例的查询。其他操作相对于这些主要操作只是稍稍做了一些变化,方便在实际中的应用。下面将对这些主要的操作

进行介绍。

/subsubsection{/_Objects/_Initialize/_information}

_Objects_Initialize_information的函数原型如下:

void _Objects_Initialize_information(

Objects_Information *information,/*需要初始化的对象信息表*/

Objects_APIs the_api, /*初始化的接口类型*/

uint32_t the_class, /*初始化的对象类型*/

uint32_t maximum, /*最大的实例数量*/

uint16_t size, /*每个实例的尺寸*/

bool is_string, /*若为真,实例的名称为字符串*/

/*名称的最大长度*/

uint32_t maximum_name_length

#if defined(RTEMS_MULTIPROCESSING)

, /*若为真,系统支持全局对象表*/

bool supports_global,

/*任务出队列时为实例执行的函数*/

Objects_Thread_queue_Extract_callout extract

#endif

);

可以看到,这个函数在多处理器和单处理器下的函数原型是不一致的。函数的参数可以参考上一小节去理解,这里

注意maximum参数。当使用32Bit的实例识别编号时,若它的最高比特位为1的话,它会将对象信息表中的

auto_extend赋值为真,如果这个比特位为0,则将其赋值为假,同时,剩下比特位表示的数值会被赋值给

信息表中的allocation_size;当使用16Bit的实例识别编号时,若第15个比特位为1的话,

也会将auto_extend赋值为真,其它动作与32Bit实例识别编号类似。

该函数按照指定的参数初始化对象信息表,函数将信息表初始化空表,即maximum为0,local_table初始化成空表,然后

通过函数_Objects_Extend_information扩展对象实例。扩展完成以后,如果系统支持多处理器并且也支持全局

实例,则初始化全局实例表。这里阅读代码时注意,实例的编号是从1开始,而C/C++中的数组都是从0开始,

RTEMS直接不使用0位置的元素,local_table申请空间时会多申请一个元素,所以,将其初始化成空表时,

它仍有一个元素,即0位置的元素。

/subsubsection{/_Objects/_Extend/_information}

_Objects_Initialize_information的函数原型如下:

void _Objects_Extend_information(

Objects_Information *information/*需要扩展的对象信息表*/);

在上一小节我们提到,初始化对象信息表的核心步骤是由该函数完成的。对可以扩展的信息表(auto_extend变量为真)和需要初始化的信息表从逻辑上讲操作是较为接近的。

主要的区别是可以扩展的信息表内存申请如果失败,可以等待下一次扩展时再试。不能扩展的信息表初始化时发生内存申请失败的情况,

必须要报错,因为再也没有机会执行这个函数了。在代码中可以看到,前者使用_Workspace_Allocate函数申请内存,而后者

使用_Workspace_Allocate_or_fatal_error函数申请内存。

支持扩展的情况下,块内存不一定申请成功,object_blocks中可能会有空指针。函数首先要确认

有没有这种情况,如果有的话,是不需要对object_blocks、inactive_per_block和local_table三个数组进行

扩展的;如果没有,需要对这三个数组进行扩展,每个表都只扩展一个元素。扩展时,先计算扩展后三个数组需要的内存,再申请内存,

并将原来的三个数组的内容拷贝到新的数组中,最后使用新数组替代老数组并释放老数组占用的内存。初始化对象信息表只是扩展一个

空的对象信息表,操作步骤上无任何区别。如图/ref{objinfotbl}所示,以上的这些操作只是对信息表本身的扩展,实例扩展是申请一个块大小的内存,

将这个内存使用双向链表初始化后,全部插入到Inactive链表中以供申请。最后更新inactive数量和inactive_per_block中

对应块的实例数量即完成所有扩展操作。

/subsubsection{/_Objects/_Shrink/_information}

_Objects_Shrink_information的函数原型如下:

void _Objects_Shrink_information(

Objects_Information *information/*需要收缩的对象信息表*/);

这个函数主要完成对未使用块的释放。搜索所有信息表中的块,对一个实例都未被使用的块,依次将块内的实例从Inactive链表中解链,

解链完成后释放该块占用的内存,并清空数组object_block内对应块的指针、清0数组inactive_per_block内对应块的实例

数量和减小未被使用实例数量inactive。

这个函数只能对支持自动扩展的对象信息表使用(auto_extend为真)。对于不支持自动扩展的对象信息表,inactive数量和

inactive_per_block对应块的实例数量并不随着申请释放发生变化。而这个函数又通过inactive_per_block对应块的实例数量判断这个块

是否为空闲块,又未对auto_extend做辅助判断,所以对不支持扩展的对象信息表不能使用该操作。

/subsubsection{/_Objects/_Allocate}

_Objects_Allocate的函数原型如下:

Objects_Control *_Objects_Allocate(

Objects_Information *information/*被申请的对象信息表*/);

函数完成向给定的对象信息表申请实例的工作,返回实例的控制块指针。函数的处理流程比较简单,从Inactive链表中将第一个节点解链,

如果对象信息表支持自动扩展,判断解链得到的实例指针是否为空,如果为空则进行实例扩展后再次申请,再次判断是否申请成功,成功则更新

表内相关的统计变量;如果不支持自动扩展,那么直接返回解链得到的实例指针,不更新表内的任何统计变量。

实例申请函数的变种_Objects_Allocate_by_index函数,它的函数原型如下:

Objects_Control *_Objects_Allocate_by_index(

Objects_Information *information, /*被申请的对象信息表*/

uint16_t the_index, /*将要申请的实例序号*/

uint16_t sizeof_control/*单个实例占用的内存大小*/);

这个函数直接在局部实例表里查找该序号的实例是否在使用中,若在使用中,则返回空指针。若未使用,则通过序号找到块中的实例,将其从

Inactive链表中解链,返回该实例。注意,申请成功后,该函数并没有更新相关的统计变量,需要用户撰写额外的代码去处理这个问题。

目前内核中虽提供了这个接口,但尚未有内部函数调用这个接口。

/subsubsection{/_Objects/_Free}

_Objects_Free的函数原型如下:

void _Objects_Free(

Objects_Information *information,/*实例将要释放到的对象信息表*/

Objects_Control *the_object /*需要释放的实例*/);

函数完成向给定的对象信息表释放实例的工作。函数将实例的插入到Inactive链表的结尾,如果支持自动扩展,则更新相关的统计变量。

然后判断表内未申请的实例数量是否超过1.5倍的allocate_size,即超过一个半的块是空闲的,超过了则使用_Objects_Shrink_information

调用紧缩信息表;如果不支持自动扩展,函数立即返回。注意该函数并未检查释放的实例是否属于该表。

/subsubsection{/_Objects/_Open}

_Objects_Open的函数原型如下:

RTEMS_INLINE_ROUTINE void _Objects_Open(

Objects_Information *information,/*被打开实例属于的对象信息表*/

Objects_Control *the_object, /*被打开实例*/

Objects_Name name /*被打开实例的名称*/);

函数将实例指针按序号赋值到指定的对象信息表内的局部实例表中,并安装实例的名称。代码简单不做赘述,这个函数分别有

_Objects_Open_u32和_Objects_Open_string两个变种,前者处理名称为整数的实例,后者处理名称为

字符串的实例。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: