RT-Thread的對象容器設(shè)計思想淺析
掃描二維碼
隨時隨地手機(jī)看文章
RT-Thread的對象容器設(shè)計思想淺析
-
1.本文概述
-
2.對象容器
-
3.對象容器的管理
-
4.手動去解析對象容器
-
5.總結(jié)
1.本文概述
最近在學(xué)習(xí)RT-Thread操作系統(tǒng)的內(nèi)核部分設(shè)計。RT-Thread的面向?qū)ο缶幊趟枷敕浅5那擅?,可以看我之前的寫的文章?
而對象(rt_object)的管理又是一個可以深入理解的地方。簡單的說,就是我們創(chuàng)建線程,或者創(chuàng)建郵箱,創(chuàng)建信號量等,最后都抽象成對象的管理。
看一下上面的圖例。用文字表述就是,所有的線程、IPC、設(shè)備創(chuàng)建的時候,都會通過鏈表被掛載在對象容器中。
2.對象容器
結(jié)合上一章的圖不難理解,對象容器就是一個二維的數(shù)組,對象的類型以及具體某個對象的鏈表。
在rt-thread中,對象容器的代碼實現(xiàn)是一個靜態(tài)的二維數(shù)組。
可以查看rt-thread\src\object.c的具體數(shù)組實現(xiàn):
#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 */ };
也就是對象容器的屬性有大小,也有對象數(shù)據(jù)鏈表。當(dāng)任意時刻,獲取到rt_object_container[RT_Object_Info_Unknown]的地址,然后解析,即可得到當(dāng)前系統(tǒng)中線程的信息、IPC的信息以及設(shè)備狀態(tài)信息,這樣去實現(xiàn)類似于PS命令就十分簡單了。
事實上,rt-thread中的list_thread、list_sem等函數(shù)的具體實現(xiàn)也是基于這個對象容器獲取到的。
3.對象容器的管理
基于RT-Thread的對象的思想,對象管理肯定有創(chuàng)建、脫離這樣的操作。
具體看一下線程創(chuàng)建的實例。
當(dāng)調(diào)用rt_thread_create函數(shù)去創(chuàng)建線程時,會調(diào)用下面函數(shù)去創(chuàng)建一個對象。
thread = (struct rt_thread *)rt_object_allocate(RT_Object_Class_Thread, name);
而這個申請對象函數(shù)的實現(xiàn)其實就是從對象容器中插入一個線程
information = rt_object_get_information(type);//得到對象容器的thread對象
接著初始化對應(yīng)的線程對象,然后插入線程對象到容器中。
/* insert object into information object list */ rt_list_insert_after(&(information->object_list), &(object->list));
然后對象容器中就存在這個線程的鏈表了,通過查詢鏈表獲得具體的線程信息。
當(dāng)線程delete的時候,也是調(diào)用這個函數(shù),從而將鏈表從對象中脫離。
rt_list_insert_after(&rt_thread_defunct, &(thread->tlist));
在rt-thread中,很多操作就是通過這種方式實現(xiàn)線程的創(chuàng)建和銷毀的。
4.手動去解析對象容器
這確實是非常有意思的事情,通過一個地址,就可以獲取系統(tǒng)的整個運(yùn)行狀態(tài)信息。
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;
通過導(dǎo)出對象容器二維數(shù)組的地址,獲取各個類型對象的列表。
比如要想獲取系統(tǒng)當(dāng)前運(yùn)行的線程相關(guān)的信息
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); }
這樣就可以解析到當(dāng)前系統(tǒng)中對象相關(guān)的信息了。其中比較重要的一個理解就是,線程鏈表其實指向的就是線程的結(jié)構(gòu)體的首地址。這樣解析起來就非常的容易了。
5.總結(jié)
通過對象容器,可以獲取系統(tǒng)信息,因為rt-thread的一切皆對象的設(shè)計思想,這種設(shè)計有很多好處。對象的管理需要相應(yīng)的容器進(jìn)行管理,這部分確實值得好好理解與學(xué)習(xí)。以后寫嵌入式代碼也需要有架構(gòu),有設(shè)計,有管理器,這樣設(shè)計出來的代碼才更加的可靠以及易于擴(kuò)展。