推荐星级:
  • 1
  • 2
  • 3
  • 4
  • 5

RT-Thread快速入门-了解内核启动流程

更新时间:2023-10-03 07:01:01 大小:381K 上传用户:sun2152查看TA发布的资源 标签:RT-Thread 下载积分:1分 评价赚积分 (如何评价?) 收藏 评论(0) 举报

资料介绍

内核是操作系统最基础也是最重要的部分。从本文开始进入 RT-Thread 内核相 关知识的学习。 首先了解内核的基础知识,对 RT-Thread 内核的设计有个初步的认识。 然后了解一下 RT-Thread 系统启动流程。 内核包括两部分:内核库、实时内核实现。 内核库 为了保证内核能够独立运行,RT-Thread 设计了一套小型的类似 C 库的函数实 现子集,根据编译器的不同 C 库的情况会有些不同。 RT-Thread 内核服务库仅提供了内核用到的 C 库函数的实现,为了避免与标准 C 库重名,在这些函数前都会加上 “rt_” 前缀。文件 src/kservice.c 部分函数 定义如下: 内核实现 实时内核的实现包括:对象管理、线程管理、调度器、线程间通信、时钟管理、 内存管理等等。内核最小的资源占用情况是 3KB ROM,1.2KB RAM。 1. 线程 线程是 RT-Thread 操作系统中最小的调度单位,线程调度算法是基于优先级的 全抢占式多线程调度算法。 RT-Thread 支持 256 个线程优先级,也可通过配置文件更改为最大支持 32 个 或 8 个线程优先级。0 优先级代表最高优先级,最低优先级留给空闲线程使用。 RT-Thread 支持创建多个具有相同优先级的线程。相同优先级的线程之间的调度, 采用时间片轮转算法,使每个线程都运行设定的时间。 调度器在切换到最高就绪线程的时间是恒定的,系统也不限制线程数量的多少, 线程数目只和硬件平台的具体内存相关 。 2.时钟管理 RT-Thread 的时钟管理以时钟节拍为基础,时钟节拍是 RT-Thread 操作系统中最 小的时钟单位。 RT-Thread 提供了两类定时器机制:  单次触发定时器。该定时器启动后只会触发一次定时器事件,然后自动停止。  周期触发定时器。这类定时器会周期性的触发定时器事件,直到用户手动停止定时 器。 3.线程间同步 RT-Thread 采用信号量、互斥量与事件集实现线程间同步。 线程通过对信号量、互斥量的获取与释放进行同步。线程同步机制支持线程按优 先级等待或按先进先出方式获取信号量或互斥量。 线程通过对事件的发送与接收进行同步;事件集支持多事件的 “或触发” 和 “与 触发”,适合于线程等待多个事件的情况。 4.线程间通信 RT-Thread 支持邮箱和消息队列等通信机制。 邮箱中一封邮件的长度固定为 4 字节大小;消息队列能够接收不固定长度的消 息,并把消息缓存在自己的内存空间中。 5.内存管理 RT-Thread 支持静态内存池管理及动态内存堆管理。 动态内存堆管理模块在系统资源不同的情况下,分别提供了面向小内存系统的内 存管理算法及面向大内存系统的 SLAB 内存管理算法。 还有一种动态内存堆管理叫做 memheap,适用于系统含有多个地址且不连续的 内存堆。使用 memheap 可以将多个内存堆 “粘贴” 在一起,让用户操作起来像 是在操作一个内存堆。 启动过程分析 RT-Thread 的启动流程与其他 RTOS 的启动有所不同。许多 RTOS 启动入口函数 为 main,而 RT-Thread 在 main 函数运行之前,系统已经完成了功能初始化。 main 函数作为用户程序的入口。 系统先从启动文件开始运行,然后进入 RT-Thread 的启动入 口 rtthread_startup() ,最后进入用户入口 main()。 以 MDK-ARM 为例,RT-Thread 启动流程,如下图所示: 系统启动后,先从汇编代码 startup_xx.s 开始运行,然后跳转到 C 代码,进行 RT-Thread 系统启动,最后进入用户程序入口 main()。 1. 扩展 main() RT-Thread 使用了 MDK 的扩展功能 $Sub$$ 和 $Super$$,使得 RT-Thread 可以 在进入 main() 之前完成系统功能初始化。关于 $Sub$$ 和 $Super$$ 的扩展功能, 可以查看 《Arm Compiler for Embedded Reference Guide Version 6.17》 https://developer.arm.com/documentation/101754/0617/armlink-Reference/A ccessing-and-Managing-Symbols-with-armlink/Use-of--Super---and--Sub---t o-patch-symbol-definitions?lang=en 指导文档内容原文如下: 对于 RT-Thread 中的 main() 函数,给其添加 $Sub$$ 的前缀符号作为一个新功能 函数 $Sub$$main ,在这个函数中添加 RT-Thread 的系统启动,进行一系列的初 始化操作。 接着在调用 $Super$$main 转到 main()函数执行 。这样可以减少用户的工作量, 不用去管 main() 之前的系统初始化操作。将主要精力用于完成用户需求的功能 模块。 在 src/components.c 文件中可以看到相关的源码: 这部分启动代码,大致可以分为四个部分:  初始化与系统相关的硬件;  初始化系统内核对象,例如定时器、调度器、信号;  创建 main 线程,在 main 线程中对各类模块依次进行初始化;  初始化定时器线程、空闲线程,并启动调度器。 2. 进入 main() 在 rtthread_startup() 函数中调用 rt_application_init() 函数,该函数会创建 一个初始化线程,也就是用户线程。 该线程的入口函数为 main_thread_entry(),在这个函数中会调 用 $Super$$main(), 进入 main()。 RT-Thread 具备自动初始化机制:初始化函数不需要被显示调用,只需要在函数 定义处,通过宏定义的方式进行声明,即可在系统启动过程中执行。 RT-Thread 的自动初始化机制使用了自定义 RTI 符号段,宏定义将需要在启动时 进行初始化的函数指针放到该段中,形成一张初始化函数表。在系统启动过程中 会遍历该表,并调用表中的函数,达到自动初始化的目的。 示例代码: 宏定义中,两个符号 ## 用于将两个字符串进行拼接。宏定义 INIT_EXPORT 将 字 符串__rt_init_ 和 函数名字符串 fn 进行拼接。 section 关键字可以将变量定义到指定的输入段中。宏定义会将__rt_init_fn 放 到指定的段中。 OK,今天先到这,下次继续。加油~

部分文件列表

文件名 大小
RT-Thread快速入门-了解内核启动流程.pdf 381K

全部评论(0)

暂无评论