目录
三、RT-Thread内核介绍
3.1、内核框架
3.1.1、线程调度
3.1.2、时钟管理
3.1.3、线程间同步
3.1.4、线程间通信
3.1.5、内存管理
3.1.6、I/O 设备管理
3.2、RTT内核启动流程
3.2.1、汇编阶段
3.2.2、C阶段
3.2.3、rtthread_startup函数
3.2.4、创建主线程
三、RT-Thread内核介绍
3.1、内核框架
内核是操作系统最基础也是最重要的部分。下图为 RT-Thread 内核架构图,内核处于硬件层之上,内核部分包括内核库、实时内核实现。
内核库是为了保证内核能够独立运行的一套小型的类似 C 库的函数实现子集。这部分根据编译器的不同自带 C 库的情况也会有些不同,当使用 GNU GCC 编译器时,会携带更多的标准 C 库实现。
实时内核的实现包括:
- 对象管理
- 线程管理及调度器
- 线程间通信管理
- 时钟管理
- 内存管理
- 设备管理
内核最小的资源占用情况是 3KB ROM,1.2KBRAM.
对于嵌入式产品,是针对某一个应用领域或者某一个应用去生产一个设备,所以它的功能要求没那么多,系统占用的资源少,成本低。
3.1.1、线程调度
线程是 RT-Thread 操作系统中最小的调度单位,线程调度算法是基于优先级的全抢占式多线程调度算法,即在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的,包括线程调度器自身。支持 256 个线程优先级(也可通过配置文件更改为最大支持 32 个或 8 个线程优先级,针对 STM32 默认配置是 32 个线程优先级),0 优先级代表最高优先级,最低优先级留给空闲线程使用;同时它也支持创建多个具有相同优先级的线程,相同优先级的线程间采用时间片的轮转调度算法进行调度,使每个线程运行相应时间;另外调度器在寻找那些处于就绪状态的具有最高优先级的线程时,所经历的时间是恒定的,系统也不限制线程数量的多少,线程数目只和硬件平台的具体内存相关。
3.1.2、时钟管理
RT-Thread 的时钟管理以时钟节拍为基础,时钟节拍是RT-Thread 操作系统中最小的时钟单位。RT-Thread 的定时器提供两类定时器机制:第一类是单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动停止。第二类是周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动的停止定时器否则将永远持续执行下去。
另外,根据超时函数执行时所处的上下文环境,RT-Thread 的定时器可以设置为 HARD_TIMER 模式或者 SOFT_TIMER 模式。
通常使用定时器定时回调函数(即超时函数),完成定时服务。用户根据自己对定时处理的实时性要求选择合适类型的定时器。
3.1.3、线程间同步
RT-Thread 采用信号量、互斥量与事件集实现线程间同步。线程通过对信号量、互斥量的获取与释放进行同步;互斥量采用优先级继承的方式解决了实时系统常见的优先级翻转问题。线程同步机制支持线程按优先级等待或按先进先出方式获取信号量或互斥量。线程通过对事件的发送与接收进行同步;事件集支持多事件的 “或触发” 和“与触发”,适合于线程等待多个事件的情况。
3.1.4、线程间通信
RT-Thread 支持邮箱和消息队列等通信机制。邮箱中一封邮件的长度固定为 4 字节大小;消息队列能够接收不固定长度的消息,并把消息缓存在自己的内存空间中。邮箱效率较消息队列更为高效。邮箱和消息队列的发送动作可安全用于中断服务例程中。通信机制支持线程按优先级等待或按先进先出方式获取。
3.1.5、内存管理
RT-Thread 支持静态内存池管理及动态内存堆管理。当静态内存池具有可用内存时,系统对内存块分配的时间将是恒定的;当静态内存池为空时,系统将申请内存块的线程挂起或阻塞掉 (即线程等待一段时间后仍未获得内存块就放弃申请并返回,或者立刻返回。等待的时间取决于申请内存块时设置的等待时间参数),当其他线程释放内存块到内存池时,如果有挂起的待分配内存块的线程存在的话,则系统会将这个线程唤醒。
动态内存堆管理模块在系统资源不同的情况下,分别提供了面向小内存系统的内存管理算法及面向大内存系统的 SLAB 内存管理算法。
还有一种动态内存堆管理叫做 memheap,适用于系统含有多个地址可不连续的内存堆。使用 memheap 可以将多个内存堆 “粘贴” 在一起,让用户操作起来像是在操作一个内存堆。
3.1.6、I/O 设备管理
RT-Thread 将 PIN、I2C、SPI、USB、UART 等作为外设设备,统一通过设备注册完成。实现了按名称访问的设备管理子系统,可按照统一的 API 界面访问硬件设备。在设备驱动接口上,根据嵌入式系统的特点,对不同的设备可以挂接相应的事件。当设备事件触发时,由驱动程序通知给上层的应用程序。
3.2、RTT内核启动流程
RT-Thread 支持多种平台和多种编译器,而 rtthread_startup() 函数是 RT-Thread 规定的统一启动入口。一般执行顺序是:系统先从启动文件开始运行,然后进入 RT-Thread 的启动 rtthread_startup() ,最后进入用户入口 main(),如下图所示:(RT-Thread Studio使用的是GCC编译器)
3.2.1、汇编阶段
Reset_Handler: //复位
/* Copy the data segment initializersfrom flash to SRAM */
movs r1, #0
b LoopCopyDataInit
CopyDataInit:
ldr r3, =_sidata
ldr r3, [r3, r1]
str r3, [r0, r1]
adds r1, r1, #4
LoopCopyDataInit:
ldr r0, =_sdata
ldr r3, =_edata
adds r2, r0, r1
cmp r2, r3
bcc CopyDataInit
ldr r2, =_sbss
b LoopFillZerobss
/* Zero fill the bss segment. */
FillZerobss:
movs r3, #0
str r3, [r2], #4
LoopFillZerobss:
ldr r3, = _ebss
cmp r2, r3
bcc FillZerobss
/* Call the clock systemintitialization function.*/
bl SystemInit
/* Call static constructors */
/* bl __libc_init_array */
/* Call the application's entrypoint.*/
bl entry
bx lr
主要过程:
- 从Flash中拷贝数据段到SRAM中
- 清空BSS段(BSS段清零)
- 初始化系统时钟(SystemInit)
- 进入entry入口
3.2.2、C阶段
【1】系统时钟初始化
system_stm32f1xx.c中的系统初始化函数,参考之前章节内容
时钟系统配置文件board.h
使用外部高速时钟,时钟源晶振8MHz,系统时钟72MHz
【2】entry入口
int entry(void)
{
rtthread_startup();
return 0;
}
3.2.3、rtthread_startup函数
int rtthread_startup(void)
{
rt_hw_interrupt_disable(); //关闭硬件中断
/*
* board level initialization
*/
rt_hw_board_init();
/* show RT-Thread version */
rt_show_version();
/* timer system initialization */
rt_system_timer_init();
/* scheduler system initialization */
rt_system_scheduler_init();
#ifdef RT_USING_SIGNALS
/* signal system initialization */
rt_system_signal_init();
#endif
/* create init_thread */
rt_application_init();
/* timer thread initialization */
rt_system_timer_thread_init();
/* idle thread initialization */
rt_thread_idle_init();
#ifdef RT_USING_SMP
rt_hw_spin_lock(&_cpus_lock);
#endif /*RT_USING_SMP*/
/* start scheduler */
rt_system_scheduler_start();
return 0;
}
主要过程:
- 初始化系统相关硬件
- 初始化系统内核对象、例如定时器、调度器、信号
- 创建主线程、定时器线程、idle线程
- 启动调度器
3.2.4、创建主线程
线程函数入口:main_thread_entry
栈大小:2048
优先级:10
同等优先级时间片轮询时间:20 个OS Tick rfconfig.h 中配置 :
#define RT_TICK_PER_SECOND 1000 Tick每秒1000次,一次的时间为1ms
//创建线程,线程函数main_thread_entry
tid =rt_thread_create("main", main_thread_entry, RT_NULL,
RT_MAIN_THREAD_STACK_SIZE,RT_MAIN_THREAD_PRIORITY, 20);
//开启线程 —— 将线程加入到系统的线程队列中,等待系统线程调度器遍历队列调用
rt_thread_startup(tid);
开启线程调度器
//选择优先级最高的线程开始调度
rt_system_scheduler_start();
void main_thread_entry(void*parameter)
{
extern int main(void);
extern int $Super$$main(void);
#ifdef RT_USING_COMPONENTS_INIT
/* RT-Thread components initialization */
rt_components_init();
#endif
#ifdef RT_USING_SMP
rt_hw_secondary_cpu_up();
#endif
/* invoke system main function */
#if defined(__CC_ARM) ||defined(__CLANG_ARM)
$Super$$main(); /* for ARMCC. */ //进入用户的main函数入口
#elif defined(__ICCARM__) ||defined(__GNUC__)
main();
#endif
}
注意:
$Sub$ $foo :定义的新功能函数,在foo()函数之前/后使用$Sub$$foo 可以添加一些新的程序代码。
$Super$ $foo :就是原始的未修补的foo函数,使用这个$Super$ $foo函数将直接跳转到foo()函数。