RT-Thread的对象容器设计思想浅析
扫描二维码
随时随地手机看文章
RT-Thread的对象容器设计思想浅析
-
1.本文概述
-
2.对象容器
-
3.对象容器的管理
-
4.手动去解析对象容器
-
5.总结
1.本文概述
最近在学习RT-Thread操作系统的内核部分设计。RT-Thread的面向对象编程思想非常的巧妙,可以看我之前的写的文章。
而对象(rt_object)的管理又是一个可以深入理解的地方。简单的说,就是我们创建线程,或者创建邮箱,创建信号量等,最后都抽象成对象的管理。
看一下上面的图例。用文字表述就是,所有的线程、IPC、设备创建的时候,都会通过链表被挂载在对象容器中。
2.对象容器
结合上一章的图不难理解,对象容器就是一个二维的数组,对象的类型以及具体某个对象的链表。
在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等函数的具体实现也是基于这个对象容器获取到的。
3.对象容器的管理
基于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中,很多操作就是通过这种方式实现线程的创建和销毁的。
4.手动去解析对象容器
这确实是非常有意思的事情,通过一个地址,就可以获取系统的整个运行状态信息。
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); }
这样就可以解析到当前系统中对象相关的信息了。其中比较重要的一个理解就是,线程链表其实指向的就是线程的结构体的首地址。这样解析起来就非常的容易了。
5.总结
通过对象容器,可以获取系统信息,因为rt-thread的一切皆对象的设计思想,这种设计有很多好处。对象的管理需要相应的容器进行管理,这部分确实值得好好理解与学习。以后写嵌入式代码也需要有架构,有设计,有管理器,这样设计出来的代码才更加的可靠以及易于扩展。