最近在学习RT-Thread操作系统的内核部分设计。RT-Thread的面向对象编程思想非常的巧妙,可以看我之前的写的文章。
而对象(rt_object)的管理又是一个可以深入理解的地方。简单的说,就是我们创建线程,或者创建邮箱,创建信号量等,最后都抽象成对象的管理。
看一下上面的图例。用文字表述就是,所有的线程、IPC、设备创建的时候,都会通过链表被挂载在对象容器中。
结合上一章的图不难理解,对象容器就是一个二维的数组,对象的类型以及具体某个对象的链表。
在rt-thread中,对象容器的代码实现是一个静态的二维数组。
可以查看rt-thread\src\object.c
的具体数组实现:
#define _OBJ_CONTAINER_LIST_INIT(c) \
{&(rt_object_container[c].object_list), &(rt_object_container[c].object_list)}
static struct rt_object_information rt_object_container[RT_Object_Info_Unknown] =
{
/* initialize object container - thread */
{RT_Object_Class_Thread, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Thread), sizeof(struct rt_thread)},
#ifdef RT_USING_SEMAPHORE
/* initialize object container - semaphore */
{RT_Object_Class_Semaphore, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Semaphore), sizeof(struct rt_semaphore)},
#endif
#ifdef RT_USING_MUTEX
/* initialize object container - mutex */
{RT_Object_Class_Mutex, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Mutex), sizeof(struct rt_mutex)},
#endif
#ifdef RT_USING_EVENT
/* initialize object container - event */
{RT_Object_Class_Event, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Event), sizeof(struct rt_event)},
#endif
#ifdef RT_USING_MAILBOX
/* initialize object container - mailbox */
{RT_Object_Class_MailBox, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MailBox), sizeof(struct rt_mailbox)},
#endif
#ifdef RT_USING_MESSAGEQUEUE
/* initialize object container - message queue */
{RT_Object_Class_MessageQueue, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MessageQueue), sizeof(struct rt_messagequeue)},
#endif
#ifdef RT_USING_MEMHEAP
/* initialize object container - memory heap */
{RT_Object_Class_MemHeap, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemHeap), sizeof(struct rt_memheap)},
#endif
#ifdef RT_USING_MEMPOOL
/* initialize object container - memory pool */
{RT_Object_Class_MemPool, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemPool), sizeof(struct rt_mempool)},
#endif
#ifdef RT_USING_DEVICE
/* initialize object container - device */
{RT_Object_Class_Device, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Device), sizeof(struct rt_device)},
#endif
/* initialize object container - timer */
{RT_Object_Class_Timer, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Timer), sizeof(struct rt_timer)},
#ifdef RT_USING_MODULE
/* initialize object container - module */
{RT_Object_Class_Module, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Module), sizeof(struct rt_dlmodule)},
#endif
};
其中rt_object_information
的定义
struct rt_object_information
{
enum rt_object_class_type type; /**< object class type */
rt_list_t object_list; /**< object list */
rt_size_t object_size; /**< object size */
};
也就是对象容器的属性有大小,也有对象数据链表。当任意时刻,获取到rt_object_container[RT_Object_Info_Unknown]
的地址,然后解析,即可得到当前系统中线程的信息、IPC的信息以及设备状态信息,这样去实现类似于PS
命令就十分简单了。
事实上,rt-thread中的list_thread
、list_sem
等函数的具体实现也是基于这个对象容器获取到的。
基于RT-Thread的对象的思想,对象管理肯定有创建、脱离这样的操作。
具体看一下线程创建的实例。
当调用rt_thread_create
函数去创建线程时,会调用下面函数去创建一个对象。
thread = (struct rt_thread *)rt_object_allocate(RT_Object_Class_Thread,
name);
而这个申请对象函数的实现其实就是从对象容器中插入一个线程
information = rt_object_get_information(type);//得到对象容器的thread对象
接着初始化对应的线程对象,然后插入线程对象到容器中。
/* insert object into information object list */
rt_list_insert_after(&(information->object_list), &(object->list));
然后对象容器中就存在这个线程的链表了,通过查询链表获得具体的线程信息。
当线程delete
的时候,也是调用这个函数,从而将链表从对象中脱离。
rt_list_insert_after(&rt_thread_defunct, &(thread->tlist));
在rt-thread中,很多操作就是通过这种方式实现线程的创建和销毁的。
这确实是非常有意思的事情,通过一个地址,就可以获取系统的整个运行状态信息。
extern struct rt_object_information rt_object_container[];
rt_uint8_t * my_addr = (rt_uint8_t *)rt_object_container;
struct rt_object_information * t32_rt_thread_container = (struct rt_object_information *)my_addr;
struct rt_object_information * t32_rt_semaphore_container = t32_rt_thread_container + 1;
struct rt_object_information * t32_rt_mutex_container = t32_rt_semaphore_container + 1;
struct rt_object_information * t32_rt_event_container = t32_rt_mutex_container + 1;
struct rt_object_information * t32_rt_mailbox_container = t32_rt_event_container + 1;
struct rt_object_information * t32_rt_messagequeue_container = t32_rt_mailbox_container + 1;
struct rt_object_information * t32_rt_memheap_container = t32_rt_messagequeue_container + 1;
struct rt_object_information * t32_rt_device_container = t32_rt_memheap_container + 1;
struct rt_object_information * t32_rt_timer_container = t32_rt_device_container + 1;
struct rt_object_information * t32_rt_module_container = t32_rt_timer_container + 1;
通过导出对象容器二维数组的地址,获取各个类型对象的列表。
比如要想获取系统当前运行的线程相关的信息
rt_list_t* thread_list;
thread_list = t32_rt_thread_container->object_list.next;
rt_thread_t rtt_thread;
rtt_thread = (rt_thread_t)(thread_list - 1);
while (1)
{
if(rtt_thread->stack_size < 20480)
{
rt_kprintf("rtt_thread->name is %s\n", rtt_thread->name);
switch (rtt_thread->stat)
{
case RT_THREAD_INIT:
rt_kprintf("RT_THREAD_INIT\n");
break;
case RT_THREAD_READY:
rt_kprintf("RT_THREAD_READY\n");
break;
case RT_THREAD_SUSPEND:
rt_kprintf("RT_THREAD_SUSPEND\n");
break;
case RT_THREAD_RUNNING:
rt_kprintf("RT_THREAD_RUNNING\n");
break;
case RT_THREAD_CLOSE:
rt_kprintf("RT_THREAD_CLOSE\n");
break;
default:
break;
}
// rt_kprintf("rtt_thread->list is %p\n", rtt_thread->list);
// rt_kprintf("rtt_thread->type is %p\n", rtt_thread->type);
// rt_kprintf("rtt_thread->stack_size is %p\n", rtt_thread->stack_size);
// rt_kprintf("rtt_thread->number_mask is %p\n", rtt_thread->number_mask);
}
else
{
break;
}
thread_list = thread_list->next;
rtt_thread = (rt_thread_t)(thread_list - 1);
}
这样就可以解析到当前系统中对象相关的信息了。其中比较重要的一个理解就是,线程链表其实指向的就是线程的结构体的首地址。这样解析起来就非常的容易了。
通过对象容器,可以获取系统信息,因为rt-thread的一切皆对象的设计思想,这种设计有很多好处。对象的管理需要相应的容器进行管理,这部分确实值得好好理解与学习。以后写嵌入式代码也需要有架构,有设计,有管理器,这样设计出来的代码才更加的可靠以及易于扩展。