title: rtthread对象容器的实现
date: 2020-10-22 15:50:29
tags: rtthread
在 RT-Thread 中,所有的数据结构都称之为对象。
对象
对象枚举定义
中线程,信号量,互斥量、事件、邮箱、消息队列、内存堆、内存池、设备和定时器在 rtdef.h 中有明显的枚举定义。
rtdef.h
/**
* 对象类型由下面的宏来使能,这些宏通常在rtconfig.h中定义
* - Thread
* - Semaphore
* - Mutex
* - Event
* - MailBox
* - MessageQueue
* - MemHeap
* - MemPool
* - Device
* - Timer
* - Module
* - Unknown
* - Static
*/
enum rt_object_class_type
{
RT_Object_Class_Thread = 0, /* 对象是线程 */
RT_Object_Class_Semaphore, /* 对象是信号量 */
RT_Object_Class_Mutex, /* 对象是互斥量 */
RT_Object_Class_Event, /* 对象是事件 */
RT_Object_Class_MailBox, /* 对象是邮箱 */
RT_Object_Class_MessageQueue, /* 对象是消息队列 */
RT_Object_Class_MemHeap, /* 对象是内存堆 */
RT_Object_Class_MemPool, /* 对象是内存池 */
RT_Object_Class_Device, /* 对象是设备 */
RT_Object_Class_Timer, /* 对象是定时器 */
RT_Object_Class_Module, /* 对象是模块 */
RT_Object_Class_Unknown, /* 对象未知 */
RT_Object_Class_Static = 0x80 /* 对象是静态对象 */
};
对象数据类型
/**
* 内核对象基础数据结构
*/
struct rt_object
{
char name[RT_NAME_MAX]; /* 内核对象的名字 */
rt_uint8_t type; /* 内核对象的类型 */
rt_uint8_t flag; /* 内核对象的状态 */
rt_list_t list; /* 内核对象的列表节点 */
};
typedef struct rt_object *rt_object_t; /*内核对象数据类型重定义 */
**对象的列表节点:**每个对象都可以通过自己的列表节点 list 将自己挂到容器列表中
在线程控制块中添加对象成员
rtdef.h
/*
*************************************************************************
* 线程结构体
*************************************************************************
*/
struct rt_thread
{
/* rt 对象 */
char name[RT_NAME_MAX]; /* 对象的名字 */
rt_uint8_t type; /* 对象类型 */
rt_uint8_t flags; /* 对象的状态 */
rt_list_t list; /* 对象的列表节点 */
rt_list_t tlist; /* 线程链表节点 */
void *sp; /* 线程栈指针 */
void *entry; /* 线程入口地址 */
void *parameter; /* 线程形参 */
void *stack_addr; /* 线程起始地址 */
rt_uint32_t stack_size; /* 线程栈大小,单位为字节 */
};
typedef struct rt_thread *rt_thread_t;
容器
在 rtt 中,每当用户创建一个对象,如线程,就会将这个对象放到一个叫做容器的地方,这样做的目的是为了方便管理。在 RT-Thread 的组件 finsh 的使用中,就需要使用到容器,通过扫描容器的内核对象来获取各个内核对象的状态,然后输出调试信息。
定义
容器就是一个数组,是一个全局变量。数据类型为 struct rt_object_information
。数组每一个成员代表一种类型
object.c
static struct rt_object_information rt_object_container[RT_Object_Info_Unknown] =
{
/* 初始化对象容器 - 线程 */
{
RT_Object_Class_Thread,
_OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Thread),
sizeof(struct rt_thread)
},
#ifdef RT_USING_SEMAPHORE
/* 初始化对象容器 - 信号量 */
{
RT_Object_Class_Semaphore,
_OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Semaphore),
sizeof(struct rt_semaphore)
},
#endif
#ifdef RT_USING_MUTEX
/* 初始化对象容器 - 互斥量 */
{
RT_Object_Class_Mutex,
_OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Mutex),
sizeof(struct rt_mutex)
},
#endif
#ifdef RT_USING_EVENT
/* 初始化对象容器 - 事件 */
{
RT_Object_Class_Event,
_OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Event),
sizeof(struct rt_event)
},
#endif
#ifdef RT_USING_MAILBOX
/* 初始化对象容器 - 邮箱 */
{
RT_Object_Class_MailBox,
_OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MailBox),
sizeof(struct rt_mailbox)
},
#endif
#ifdef RT_USING_MESSAGEQUEUE
/* 初始化对象容器 - 消息队列 */
{
RT_Object_Class_MessageQueue,
_OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MessageQueue),
sizeof(struct rt_messagequeue)
},
#endif
#ifdef RT_USING_MEMHEAP
/* 初始化对象容器 - 内存堆 */
{
RT_Object_Class_MemHeap,
_OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemHeap),
sizeof(struct rt_memheap)
},
#endif
#ifdef RT_USING_MEMPOOL
/* 初始化对象容器 - 内存池 */
{
RT_Object_Class_MemPool,
_OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemPool),
sizeof(struct rt_mempool)
},
#endif
#ifdef RT_USING_DEVICE
/* 初始化对象容器 - 设备 */
{
RT_Object_Class_Device,
_OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Device), sizeof(struct rt_device)},
#endif
/* 初始化对象容器 - 定时器 */
/*
{
RT_Object_Class_Timer,
_OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Timer),
sizeof(struct rt_timer)
},
*/
#ifdef RT_USING_MODULE
/* 初始化对象容器 - 模块 */
{
RT_Object_Class_Module,
_OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Module),
sizeof(struct rt_module)
},
#endif
};
目前没有加定时器,先注释掉。
对象容器示意图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7o9B1pB3-1608216037994)(https://i.loli.net/2020/10/22/RUHcTFwzD9lGhI8.png)]
对象信息数据类型
容器是一个全局变量的数组,数据类型为 struct rt_object_information
,这是一个结构体类型,包含对象的三个信息,分别为对象类型、对象列表节点头和对象的大小。
rtdef.h
/**
* 内核对象信息结构体
*/
struct rt_object_information
{
enum rt_object_class_type type; /* 对象类型 */
rt_list_t object_list; /* 对象列表节点 */
rt_size_t object_size; /* 对象大小 */
};
rt_object_info_type枚举
object.c
/*
* 对象容器数组的下标定义,决定容器的大小
*/
enum rt_object_info_type
{
RT_Object_Info_Thread = 0, /* 对象是线程 */
#ifdef RT_USING_SEMAPHORE
RT_Object_Info_Semaphore, /* 对象是信号量 */
#endif
#ifdef RT_USING_MUTEX
RT_Object_Info_Mutex, /* 对象是互斥量 */
#endif
#ifdef RT_USING_EVENT
RT_Object_Info_Event, /* 对象是事件 */
#endif
#ifdef RT_USING_MAILBOX
RT_Object_Info_MailBox, /* 对象是邮箱 */
#endif
#ifdef RT_USING_MESSAGEQUEUE
RT_Object_Info_MessageQueue, /* 对象是消息队列 */
#endif
#ifdef RT_USING_MEMHEAP
RT_Object_Info_MemHeap, /* 对象是内存堆 */
#endif
#ifdef RT_USING_MEMPOOL
RT_Object_Info_MemPool, /* 对象是内存池 */
#endif
#ifdef RT_USING_DEVICE
RT_Object_Info_Device, /* 对象是设备 */
#endif
RT_Object_Info_Timer, /* 对象是定时器 */
#ifdef RT_USING_MODULE
RT_Object_Info_Module, /* 对象是模块 */
#endif
RT_Object_Info_Unknown, /* 对象未知 */
};
_OBJ_CONTAINER_LIST_INIT()宏定义
_OBJ_CONTAINER_LIST_INIT()是一个带参宏,用于初始化一个节点 list
object.c
#define _OBJ_CONTAINER_LIST_INIT(c) \
{&(rt_object_container[c].object_list), &(rt_object_container[c].object_list)}
容器的接口实现
获取指定类型的对象信息
从容器中获取指定类型的对象的信息
/**
* 获取指定类型的对象信息
*
* @param type 对象类似
* @return 对象信息 or RT_NULL
*/
struct rt_object_information *rt_object_get_information(enum rt_object_class_type type)
{
int index;
for (index = 0; index < RT_Object_Info_Unknown; index ++)
if (rt_object_container[index].type == type) return &rt_object_container[index];
return RT_NULL;
}
容器在定义的时候,大小是被固定的,由 RT_Object_Info_Unknown 这个枚举值决定,但容器里面的成员是否初始化就不一定了,其中线程和定时器这两个对象默认会被初始化,剩下的其它对象由对应的宏决定。rt_object_get_information()会遍历整个容器对象,如果对象的类型等于我们指定的类型,那么就返回该容器成员的地址,地址的类型为 struct rt_object_information。
对象的初始化
- 从容器里拿到对应对象列表头指针
- 设置对象类型为静态
- 设置对象名字,一般与线程名字一样
- 将对象插入到容器的对应列表中
object.c
/**
* 该函数将初始化对象并将对象添加到对象容器中
*
* @param object 要初始化的对象
* @param type 对象的类型
* @param name 对象的名字,在整个系统中,对象的名字必须是唯一的
*/
void rt_object_init(struct rt_object *object,
enum rt_object_class_type type,
const char *name)
{
register rt_base_t temp;
struct rt_object_information *information;
/* 获取对象信息,即从容器里拿到对应对象列表头指针 */
information = rt_object_get_information(type);
/* 设置对象类型为静态 */
object->type = type | RT_Object_Class_Static;
/* 拷贝名字 */
rt_strncpy(object->name, name, RT_NAME_MAX);
/* 关中断 */
temp = rt_hw_interrupt_disable();
/* 将对象插入到容器的对应列表中,不同类型的对象所在的列表不一样 */
rt_list_insert_after(&(information->object_list), &(object->list));
/* 使能中断 */
rt_hw_interrupt_enable(temp);
}
kservice.c
/**
* 该函数将指定个数的字符串从一个地方拷贝到另外一个地方
*
* @param dst 字符串拷贝的目的地
* @param src 字符串从哪里拷贝
* @param n 要拷贝的最大长度
*
* @return the result
*/
char *rt_strncpy(char *dst, const char *src, rt_ubase_t n)
{
if (n != 0)
{
char *d = dst;
const char *s = src;
do
{
if ((*d++ = *s++) == 0)
{
/* NUL pad the remaining n-1 bytes */
while (--n != 0)
*d++ = 0;
break;
}
} while (--n != 0);
}
return (dst);
}
在容器中插入两个线程对象如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b07iLGnV-1608216037999)(https://i.loli.net/2020/10/22/JykelF2bR3CAVqZ.png)]
调用对象初始化函数
对象初始化函数在线程初始化函数里面被调用。在线程初始化之后,线程通过自身的 list 节点将自身挂到容器的对象列表中。
rt_err_t rt_thread_init(struct rt_thread *thread,
const char *name,
void (*entry)(void *parameter),
void *parameter,
void *stack_start,
rt_uint32_t stack_size)
{
/* 线程对象初始化 */
/* 线程结构体开头部分的成员就是rt_object_t类型 */
rt_object_init((rt_object_t)thread, RT_Object_Class_Thread, name);
rt_list_init(&(thread->tlist));
thread->entry = (void *)entry;
thread->parameter = parameter;
thread->stack_addr = stack_start;
thread->stack_size = stack_size;
/* 初始化线程栈,并返回线程栈指针 */
thread->sp = (void *)rt_hw_stack_init( thread->entry,
thread->parameter,
(void *)((char *)thread->stack_addr + thread->stack_size - 4) );
return RT_EOK;
}