内核对象管理框架
RT-Thread采用内核对象管理系统来访问/管理所有内核对象,内核对象包含了内核中绝大部分设施,这些内核对象可以是静态分配的静态对象,也可以是从系统内存堆中分配的动态对象。
RT-Thread内核对象包括:线程,信号量,互斥量,事件等。
对象容器中包含了每类内核对象的信息,包括对象类型,大小等。
对象容器给每类内核对象分配了一个链表,所有的内核对象都被链接到该链表上。
RT-Thread中各类内核对象的派生和继承关系。
对于每一种具体内核对象和对象控制块,除了基本结构外,还有自己的扩展属性(私有属性)。
例如,对于线程控制块,在基类对象基础上进行扩展,增加了线程状态、优先级等属性。
这些属性在基类对象的操作中不会用到,只有在与具体线程相关的操作中才会使用。
因此,从面向对象的观点,可以认为每一种具体对象是抽象对象的派生,继承了基本对象的属性并在此基础上扩展了自己相关的属性。
在对象管理模块中,定义了通用的数据结构,用来保存各种对象的共同属性,各种具体对象只需要在此基础上加上自己的某些特别的属性,就可以清楚的表示自己的特征。
这种设计方法的优点有:
- 提高了系统的可重用性和扩展性,增加新的对象类别很容易,只需要继承通用对象的属性再加少量扩展即可。
- 提供统一的对象操作方式,简化了各种具体对象的操作,提高了系统的可靠性。
上图中由对象控制块 rt_object 派生出来的有:线程对象、内存池对象、定时器对象、设备对象和 IPC 对象(IPC:Inter-Process Communication,进程间通信。在 RT-Thread 实时操作系统中,IPC 对象的作用是进行线程间同步与通信);由 IPC 对象派生出信号量、互斥量、事件、邮箱与消息队列、信号等对象。
对象控制块
内核对象控制块的数据结构:
struct rt_object
{char name[RT_NAME_MAX];rt_uint8_t type;rt_uint8_t flag;rt_list_t list;
};
目前内核对象支持的类型如下:
enum rt_object_class_type
{RT_Object_Class_Thread = 0, /* 对象为线程类型 */
#ifdef RT_USING_SEMAPHORERT_Object_Class_Semaphore, /* 对象为信号量类型 */
#endif
#ifdef RT_USING_MUTEXRT_Object_Class_Mutex, /* 对象为互斥量类型 */
#endif
#ifdef RT_USING_EVENTRT_Object_Class_Event, /* 对象为事件类型 */
#endif
#ifdef RT_USING_MAILBOXRT_Object_Class_MailBox, /* 对象为邮箱类型 */
#endif
#ifdef RT_USING_MESSAGEQUEUERT_Object_Class_MessageQueue, /* 对象为消息队列类型 */
#endif
#ifdef RT_USING_MEMPOOLRT_Object_Class_MemPool, /* 对象为内存池类型 */
#endif
#ifdef RT_USING_DEVICERT_Object_Class_Device, /* 对象为设备类型 */
#endifRT_Object_Class_Timer, /* 对象为定时器类型 */
#ifdef RT_USING_MODULERT_Object_Class_Module, /* 对象为模块 */
#endifRT_Object_Class_Unknown, /* 对象类型未知 */RT_Object_Class_Static = 0x80 /* 对象为静态对象 */
};
那么对象类型的最高位将是 1(是 RT_Object_Class_Static 与其他对象类型的与操作),否则就是动态对象,系统最多能够容纳的对象类别数目是 127 个。
内核对象管理方式
内核对象容器的数据结构:
struct rt_object_information
{enum rt_object_class_type type;rt_list_t object_list;rt_size_t object_size;
}
一类对象由一个rt_object_information结构体来管理,每一个这类对象的具体实例都通过链表的形式挂接在object_list上。
而这一类对象的内存块尺寸由object_size标识出来(每一类对象的具体实例,它们占有的内存块大小是相同的)。
初始化对象
在使用一个未初始化的静态对象前必须先对其进行初始化。
初始化对象使用以下接口:
void rt_object_init(struct rt_object* object,enum rt_object_class_type type, const char* name)
当调用这个函数进行对象初始化时,系统会把这个对象放置到对象容器中进行管理,即初始化对象的一些参数,然后把这个对象节点插入到对象容器的对象链表中。
脱离对象
从内核对象管理器中脱离一个对象。
void rt_object_detach(rt_object_t object);
调用该接口,可使得一个静态内核对象从内核对象容器中脱离出来,即从内核对象容器链表上删除相应的对象节点。
对象脱离后,对象占用的内存并不会释放。
RT-Thread内核配置示例
RT-Thread的一个重要特性是高度可裁剪性,支持对内核进行精细调整,对组件进行灵活拆卸。
配置主要是通过修改工程目录下的rtconfig.h文件来进行,用户可以通过打开/关闭该文件中的宏定义来对代码进行条件编译,最终达到系统配置和裁剪的目的,如下:
/* 表示内核对象的名称的最大长度,若代码中对象名称的最大长度大于宏定义的长度,* 多余的部分将被截掉。*/
#define RT_NAME_MAX 8/* 字节对齐时设定对齐的字节个数。常使用 ALIGN(RT_ALIGN_SIZE) 进行字节对齐。*/
#define RT_ALIGN_SIZE 4/* 定义系统线程优先级数;通常用 RT_THREAD_PRIORITY_MAX-1 定义空闲线程的优先级 */
#define RT_THREAD_PRIORITY_MAX 32/* 定义时钟节拍,为 100 时表示 100 个 tick 每秒,一个 tick 为 10ms */
#define RT_TICK_PER_SECOND 100/* 检查栈是否溢出,未定义则关闭 */
#define RT_USING_OVERFLOW_CHECK/* 定义该宏开启 debug 模式,未定义则关闭 */
#define RT_DEBUG
/* 开启 debug 模式时:该宏定义为 0 时表示关闭打印组件初始化信息,定义为 1 时表示启用 */
#define RT_DEBUG_INIT 0
/* 开启 debug 模式时:该宏定义为 0 时表示关闭打印线程切换信息,定义为 1 时表示启用 */
#define RT_DEBUG_THREAD 0/* 定义该宏表示开启钩子函数的使用,未定义则关闭 */
#define RT_USING_HOOK/* 定义了空闲线程的栈大小 */
#define IDLE_THREAD_STACK_SIZE 256