1、RTT框图
内核库是为了保证内核能够独立运行的一套小型的类似 C 库(不同C库实现可能不同)的函数实现子集。仅提供内核用到的一小部分 C 库函数实现,为了避免与标准 C 库重名,在这些函数前都会添加上 rt_ 前缀。
2、RTT启动流程
RT-Thread 支持多种平台和多种编译器,而 rtthread_startup() 函数是 RT-Thread 规定的统一启动入口。顺序:系统从启动文件开始运行,再进入 RT-Thread 的启动 rtthread_startup() ,最后进入用户入口 main()
3、内存分布
//示例①:msg_ptr 指针指向的 128 字节内存空间位于动态内存堆空间中。
rt_uint8_t* msg_ptr;
msg_ptr = (rt_uint8_t*) rt_malloc (128);
rt_memset(msg_ptr, 0, 128);
//示例②:
//常量形式的全局变量放置在 RO 段中,是只读属性的
const static rt_uint32_t sensor_enable = 0x000000FE;
//一些全局变量存放于 RW 段和 ZI 段中
//ZI 段存放的系统未初始化的全局变量(所以会自动初始化为0)
rt_uint32_t sensor_value;
//RW 段存放的是具有初始值的全局变量
rt_bool_t sensor_inited = RT_FALSE;
4、自动初始化机制
初始化函数不需要被显式调用,而只需要在函数定义处通过宏定义的方式进行申明, 就会在系统启动过程中被执行。
int rt_hw_usart_init(void) /* 串 口 初 始 化 函 数 */
{
... ...
/* 注 册 串 口 1 设 备 */
rt_hw_serial_register(&serial1, "uart1",
RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
uart);
return 0;
}
//使用自动初始化功能
INIT_BOARD_EXPORT(rt_hw_usart_init); /* 使 用 组 件 自 动 初 始 化 机 制 */
rt_components_board_init() 初始化硬件环境,执行该函数会遍历 INIT_BOARD_EXPORT(fn) 申明的初始化函数表,并调用各个函数。
①“board init functions” 为所有通过 INIT_BOARD_EXPORT(fn) 申明的初始化函数。
rt_components_init() 函数会在操作系统运行起来之后创建的 main 线程里被调用执行
此时硬件环境和操作系统已经初始化完成,该函数会遍历通过剩下的几个宏申明的初始化函数表。
②“pre-initialization functions” 为所有通过 INIT_PREV_EXPORT(fn) 申明的初始化函数。
③“device init functions” 为所有通过 INIT_DEVICE_EXPORT(fn) 申明的初始化函数。
④“components init functions” 为所有通过 INIT_COMPONENT_EXPORT(fn) 申明的初始化函数。
⑤“enviroment init functions” 为所有通过 INIT_ENV_EXPORT(fn) 申明的初始化函数。
⑥“application init functions” 为所有通过 INIT_APP_EXPORT(fn) 申明的初始化函数。
自动初始化机制使用了自定义 RTI 符号段,将需要在启动时进行初始化的函数指针放到该段中,形成一张初始化函数表,该符号段位于内存分布的 RO 段中,其中的所有函数在系统初始化时会被遍历自动调用。
5、内核对象模型
内核对象:线程,信号量,互斥量,事件,邮箱,消息队列和定时器,内存池,设备 驱动等;
5.1 静态内核对象和动态内核对象
静态内核对象通常放在 RW 段和 ZI 段中,在系统启动后在程序中初始化;
动态内核对象是从内存堆中创建的,而后手工做初始化。
从以下例子可以看出,两种创建线程的方式,静态方式指定了栈空间,而动态方式没有
线程1的线程控制块与栈空间是编译时决定的,不存在初始值,统一放在未初始化数据段ZI中;会占用 RAM 空间,不依赖于内存堆管理器,内存分配时间确定。
线程2运行中线程控制块和栈空间用到的空间是动态分配的,依赖于内存堆管理器,运行时申请 RAM 空间,当对象被删除后空间被释放。
/* 线 程 1 的 对 象 和 运 行 时 用 到 的 栈 */
static struct rt_thread thread1;
static rt_uint8_t thread1_stack[512];
/*......线程入口函数省略......*/
/* 线 程 例 程 初 始 化 */
int thread_sample_init()
{
rt_thread_t thread2_ptr;
rt_err_t result;
/* 初 始 化 线 程 1 (静态)*/
/* 线 程 的 入 口 是 thread1_entry, 参 数 是 RT_NULL
* 线 程 栈 是 thread1_stack
* 优 先 级 是 200, 时 间 片 是 10 个 OS Tick
*/
result = rt_thread_init(&thread1,
"thread1",
thread1_entry, RT_NULL,
&thread1_stack[0], sizeof(thread1_stack),
200, 10);
/* 启 动 线 程 */
if (result == RT_EOK) rt_thread_startup(&thread1);
/* 创 建 线 程 2 (动态)*/
/* 线 程 的 入 口 是 thread2_entry, 参 数 是 RT_NULL
* 栈 空 间 是 512, 优 先 级 是 250, 时 间 片 是 25 个 OS Tick
*/
thread2_ptr = rt_thread_create("thread2",
thread2_entry, RT_NULL,
512, 250, 25);
/* 启 动 线 程 */
if (thread2_ptr != RT_NULL) rt_thread_startup(thread2_ptr);
return 0;
}
5.2 内核对象管理架构
如果是静态对象,则对象类型的最高位将是1(对象类型与上RT_Object_Class_Static = 0x80)
5.3 内核对象的管理方式
5.3.1 初始化
在使用一个未初始化的静态对象前必须先对其进行初始化。
调用这个函数初始化对象的 一些参数,再把这个对象节点插入到对象容器的对象链表中管理。
void rt_object_init(struct rt_object* object ,
enum rt_object_class_type type ,
const char* name)
5.3.2 脱离对象
调用该接口可使得一个静态内核对象从内核对象容器链表上删除相应的对象节点。对象脱离后,对象占用的内存并不会被释放。
void rt_object_detach(rt_object_t object);
5.2.3 分配对象
上述接口都是面向对象内存块已经有的情况下,而动态对象则可以在需要时申请,不需要时释放出内存空间给其他应用使用。
在调用以下接口时,系统首先根据对象类型来获取对象信息(尤其是大小),而后从内存堆中分配所对应大小的内存空间,再对该对象进行必要的初始化,最后将其插入到对应对象容器链表中。
rt_object_t rt_object_allocate(enum rt_object_class_type type ,
const char* name)
5.3.4 删除对象
对于一个动态对象,当不再使用时调用如下接口脱离对象并释放资源
void rt_object_delete(rt_object_t object);
5.3.5 辨别对象
判断指定对象是否是静态内核对象(系统对象)
RT-Thread 操作系统 中,系统对象类型标识上 RT_Object_Class_Static 位置位(最高位是1)。通常使用 rt_object_init() 方式初始化的对象都是系统对象。
rt_err_t rt_object_is_systemobject(rt_object_t object);
6、内核裁剪配置/常用宏
配置主要是通过修改工程目录下的 rtconfig.h 文件来进行,用户可以通过打开 / 关闭该文件中的宏定 义来对代码进行条件编译,最终达到系统配置和裁剪的目的(官方P46)。
呃,做了一部分,感觉不如官方文档。。。。。