RT-Thread内核——内核基础(上)

news2024/9/28 5:27:32

1、内核简介

内核是操作系统的核心,是操作系统最基础也是最重要的部分,主要负责系统的线程、线程间通信、系统时钟、中断以及内存等。其架构图如下:
在这里插入图片描述

2、线程调度

线程是RT-Thread操作系统中最小的调度单位,线程调度算法的基于优先级的全抢占式多线程调度算法,即在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码外是不可抢占的,其他都可以抢占,包括线程调度器自身。线程调度算法支持256个线程优先级,0优先级代表最高优先级,最低优先级留给空闲线程使用;对于相同优先级的线程,采用时间片的轮转调度算法进行调度,使每个线程运行相应时间;调度器在寻找那些处于就绪态的具有最高优先级的线程时,所经历的时间是恒定的,系统不限制线程数量的多少,线程数目只和硬件平台的具体内存相关,这个具体的内存要去掉实际程序中已经用掉的内存,实际的代码会用掉一部分内存,因此不能只看硬件的参数,还需要考虑代码对内存的占用情况。

3、 时钟管理

RT-Thread的时钟管理是以时钟节拍为基础的,时钟节拍的RT-Thread中最小的时钟单位。RT-Thread提供两种定时器的机制:一是单次触发定时器,此定时器在启动之后只会触发一次定时器事件,然后定时器自动停止;二是周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动的停止定时器,否则将永远持续执行下去。
根据超时函数执行时的上下文,RT-Thread的定时器可以设置为HARD_TIMER模式和SOFT_TIMER模式。

4、线程间同步

RT-Thread采用信号量互斥量事件实现线程间同步。线程通过对信号量、互斥量的获取与释放进行同步;互斥量采用优先级继承的方式解决了实时系统常见的优先级反转问题。线程同步机制支持线程按优先级等待方式获取信号量或互斥量。线程通过对事件的发送与接受进行同步;事件集支持多事件的“或触发”和“与触发”,适合于线程等待多个事件的情况。

5、线程间通信

RT-Thread 支持邮箱消息队列等通信机制。邮箱中一封邮件的长度固定为 4 字节大小;消息队列能够接收不固定长度的消息,并把消息缓存在自己的内存空间中。邮箱效率较消息队列更为高效。邮箱和消息队列的发送动作可安全用于中断服务例程中。通信机制支持线程按优先级等待方式获取。

6、内存管理

RT-Thread支持静态内存池管理动态内存堆管理。当静态内存池具有可用内存时,系统对内存块分配的时间是恒定的;当静态内存池为空时,系统将申请内存块的线程挂起或者阻塞掉(线程等待一段时间后仍未获得内存块就放弃申请并返回,或者立刻返回。等待的时间取决去申请内存块时设置的等待时间参数),当其他线程释放内存块到内存池时,如有挂起的待分配的线程存在的话,则系统会将这个线程唤醒。
动态内存堆管理模块在系统资源不同的情况下,分别提供了面向小内存管理算法及面向大内存系统的SLAB内存管理算法。
另一种动态内存堆管理叫做memheap,适用于系统含有多个地址且不连续的内存堆。使用memheap可以将多个内存堆“粘贴”在一起,让用户操作起来像是在操作一个内存堆。

7、I/O设备管理

RT-Thread 将 PIN、I2C、SPI、USB、UART 等作为外设设备,统一通过设备注册完成。实现了按名称访问的设备管理子系统,可按照统一的 API 界面访问硬件设备。在设备驱动接口上,根据嵌入式系统的特点,对不同的设备可以挂接相应的事件。当设备事件触发时,由驱动程序通知给上层的应用程序。

8、RT-Thread启动流程

RT-Thread 支持多种平台和多种编译器,而 rtthread_startup() 函数是 RT-Thread 规定的统一启动入口。一般执行顺序是:系统先从启动文件开始运行,然后进入 RT-Thread 的启动函数 rtthread_startup() ,最后进入用户入口函数 main(),如下图所示:
在这里插入图片描述
rtthread_startup() 函数,其中 rtthread_startup() 函数的代码如下所示:

int rtthread_startup(void)
{
    rt_hw_interrupt_disable();

    /* 板级初始化:需在该函数内部进行系统堆的初始化 */
    rt_hw_board_init();

    /* 打印 RT-Thread 版本信息 */
    rt_show_version();

    /* 定时器初始化 */
    rt_system_timer_init();

    /* 调度器初始化 */
    rt_system_scheduler_init();

#ifdef RT_USING_SIGNALS
    /* 信号初始化 */
    rt_system_signal_init();
#endif

    /* 由此创建一个用户 main 线程 */
    rt_application_init();

    /* 定时器线程初始化 */
    rt_system_timer_thread_init();

    /* 空闲线程初始化 */
    rt_thread_idle_init();

    /* 启动调度器 */
    rt_system_scheduler_start();

    /* 不会执行至此 */
    return 0;
}

这部分启动代码,大致可以分为四个部分:
(1)初始化与系统相关的硬件;
(2)初始化系统内核对象,例如定时器、调度器、信号;
(3)创建 main 线程,在 main 线程中对各类模块依次进行初始化;
(4)初始化定时器线程、空闲线程,并启动调度器。
启动调度器之前,系统所创建的线程在执行 rt_thread_startup() 后并不会立马运行,它们会处于就绪状态等待系统调度;待启动调度器之后,系统才转入第一个线程开始运行,根据调度规则,选择的是就绪队列中优先级最高的线程。
rt_hw_board_init() 中完成系统时钟设置,为系统提供心跳、串口初始化,将系统输入输出终端绑定到这个串口,后续系统运行信息就会从串口打印出来。

9、RT-Thread程序内存分布

一般MCU包含的存储空间有:片内Flash与片内RAM,RAM相当于内存,Flash相当于硬盘。编译器会将程序分为几个部分,分别存储在MCU不同的位置。
Keil 工程在编译完之后,会有相应的程序所占用的空间提示信息,如下所示:

linking...
Program Size: Code=54872 RO-data=8656 RW-data=764 ZI-data=21812  
After Build - User command #1: fromelf --bin .\build\rtthread-stm32.axf --output rtthread.bin
".\build\rtthread-stm32.axf" - 0 Error(s), 0 Warning(s).
Build Time Elapsed:  00:00:14

上面提到的 Program Size 包含以下几个部分:
1)Code:代码段,存放程序的代码部分;
2)RO-data:只读数据段,存放程序中定义的常量;
3)RW-data:读写数据段,存放初始化为非 0 值的全局变量;
4)ZI-data:0 数据段,存放未初始化的全局变量及初始化为 0 的变量;
编译完工程会生成一个.map 的文件,该文件说明了各个函数占用的尺寸和地址,在文件的最后几行也说明了上面几个字段的关系:

==============================================================================

    Total RO  Size (Code + RO Data)                63528 (  62.04kB)
    Total RW  Size (RW Data + ZI Data)             22576 (  22.05kB)
    Total ROM Size (Code + RO Data + RW Data)      63676 (  62.18kB)

==============================================================================

1)RO Size 包含了 Code 及 RO-data,表示程序占用 Flash 空间的大小;
2)RW Size 包含了 RW-data 及 ZI-data,表示运行时占用的 RAM 的大小;
3)ROM Size 包含了 Code、RO-data 以及 RW-data,表示烧写程序所占用的 Flash 空间的大小;
程序运行之前,需要有文件实体被烧录到 STM32 的 Flash 中,一般是 bin 或者 hex 文件,该被烧录文件称为可执行映像文件。如下图左边部分所示,是可执行映像文件烧录到 STM32 后的内存分布,它包含 RO 段和 RW 段两个部分:其中 RO 段中保存了 Code、RO-data 的数据,RW 段保存了 RW-data 的数据,由于 ZI-data 都是 0,所以未包含在映像文件中。
STM32 在上电启动之后默认从 Flash 启动,启动之后会将 RW 段中的 RW-data(初始化的全局变量)搬运到 RAM 中,但不会搬运 RO 段,即 CPU 的执行代码从 Flash 中读取,另外根据编译器给出的 ZI 地址和大小分配出 ZI 段,并将这块 RAM 区域清零。
在这里插入图片描述
其中动态内存堆为未使用的 RAM 空间,应用程序申请和释放的内存块都来自该空间。而一些全局变量则是存放于 RW 段和 ZI 段中,RW 段存放的是具有初始值的全局变量(而常量形式的全局变量则放置在 RO 段中,是只读属性的),ZI 段存放的系统未初始化的全局变量。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1151190.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

线性代数 第二章 矩阵

一、概念 mxn个数排成的m行n列的表格 二、运算法则 三、初等变换 (1)用非零常数k乘矩阵的某一行(列); (2)互换矩阵某两行(列)的位置; (3&am…

服务端测试开发必备技能:Mock测试

什么是mock测试 Mock 测试就是在测试活动中,对于某些不容易构造或者不容易获取的数据/场景,用一个Mock对象来创建以便测试的测试方法。 Mock测试常见场景 无法控制第三方系统接口的返回,返回的数据不满足要求依赖的接口还未开发完成&#…

接入百度地图api

注册百度账号,进入百度地图百度地图开放平台 | 百度地图API SDK | 地图开发 点击开发文档,比如js开发选择javascript API,然后跟着向下走,官方文档挺详细 先获取账号与密钥 自用Referer设置 * 即可 复制AK密钥导入自己的html文件即…

MVCC(多版本并发控制)

一、什么是MVCC MVCC是为了解决数据库在不加锁的前提下提升并发性和读取效率的一种思想 数据库有已下几种并发情况 读-读:不会产生并发问题读-写:发生隔离性问题,可能导致脏读、幻读、不可重复度写-写:可能存在数据丢失 为了防…

京东科技埋点数据治理和平台建设实践 | 京东云技术团队

导读 本文核心内容聚焦为什么要埋点治理、埋点治理的方法论和实践、奇点一站式埋点管理平台的建设和创新功能。读者可以从全局角度深入了解埋点、埋点治理的整体思路和实践方法,落地的埋点工具和创新功能都有较高的实用参考价值。遵循埋点治理的方法论,…

sd模型测试之又纯又欲的Copax Anime XL动漫大模型

除了各种美女图外,AI绘画大模型中,最受欢迎的是动漫。 动漫又分好几种,幼儿向、热血向、成人向等。 之前我推荐了几个风格不同的动漫大模型,今天推荐一个成人向的动漫大模型:Copax Anime XL。 当然了,成…

One-to-N N-to-One: Two Advanced Backdoor Attacks Against Deep Learning Models

One-to-N & N-to-One: Two Advanced Backdoor Attacks Against Deep Learning Models----《一对N和N对一:针对深度学习模型的两种高级后门攻击》 1对N: 通过控制同一后门的不同强度触发多个后门 N对1: 只有当所有N个后门都满足时才会触发…

【JAVA学习笔记】54 - 集合 - Set类、HashSet类(难点)

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter14/src/com/yinhai/set_ Set类 一、基本介绍 1.无序(添加和取出的顺序不一致) ,没有索引[后面演示] 2.不允许重复元素,所以最多包含一个null 3.JDK API中Set接口的实现类有: …

SQL BETWEEN运算符

SQL BETWEEN 运算符 BETWEEN运算符用于选取介于两个值之间的数据范围内的值。 BETWEEN运算符选择给定范围内的值。值可以是数字,文本或日期。 BETWEEN运算符是包含性的:包括开始和结束值,且开始值需小于结束值。 SQL BETWEEN 语法 SELECT …

【蓝桥杯选拔赛真题06】C++数字评级 青少年组蓝桥杯C++选拔赛真题 STEMA比赛真题解析

目录 C/C++数字评级 一、题目要求 1、编程实现 2、输入输出 二、算法分析 <

gma 2 教程(三)坐标参考系统:2.基准面/椭球体

安装 gma&#xff1a;pip install gma 地球是一个近似于椭球体的三维物体&#xff0c;而地球上的各种测量和计算都需要一个基准面来进行。基准面是一个虚拟的平面&#xff0c;用于测量和计算地球上的各种物理量。在地球科学中&#xff0c;基准面通常是一个参考椭球体&#xff0…

rabbitmq安装、基本使用

docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.12-management docker会自己下载&#xff0c;然后运行 进入docker&#xff1a; docker exec -it rabbitmq bash 进入容器&#xff0c;重启rabbitmq&#xff1a;rabbitmq-server restart 感觉所有的…

基于平衡优化器算法的无人机航迹规划-附代码

基于平衡优化器算法的无人机航迹规划 文章目录 基于平衡优化器算法的无人机航迹规划1.平衡优化器搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用平衡优化器算法来优化无人机航迹…

前端如何实现多种方式圆形可点击区域

前言 四种方式都可以实现在圆形区域内添加点击事件&#xff0c;选择哪种方式可以根据具体情况选择。其中使用canvas实现的方式可以更好地适用于需要绘制复杂图形的情况下。 方式一&#xff1a;border-radius 使用CSS的border-radius属性创建圆形区域&#xff0c;然后通过绑定点…

Spring Boot 3系列之一(初始化项目)

近期&#xff0c;JDK 21正式发布&#xff0c;而Spring Boot 3也推出已有一段时间。作为这两大技术领域的新一代标杆&#xff0c;它们带来了许多令人振奋的新功能和改进。尽管已有不少博客和文章对此进行了介绍&#xff0c;但对于我们这些身处一线的开发人员来说&#xff0c;有些…

Generalized Zero-Shot Learning With Multi-Channel Gaussian Mixture VAE

L D A _{DA} DA​最大化编码后两种特征分布之间的相似性 辅助信息 作者未提供代码

通过引入插件Grid Masschange实现批量修改交互网格数据

现状描述: 很多时候我们需要对交互式网格进行数据更新&#xff0c;单个或少量的数据还好&#xff0c;一旦数据量过大&#xff0c;APEX现有的原生手动输入就不能满足需求&#xff0c;既浪费人力又浪费时间&#xff0c;白白损失了劳动成本&#xff0c;应对这种情况&#xff0c;有…

flink 反压原理

背景 在flink中由于数据倾斜或者数据处理速率的不匹配&#xff0c;很容易引起反压&#xff0c;本文就看一下flink反压的原理 flink反压原理 flink全流程pineline的反压实现其实依赖于TaskManager之间的反压和TaskManager内部的反压来实现 1.TaskManager之间的反压 2.Task…

Linux指令【上】

目录 目录结构 ls cd stat touch mkdir whoami 查看当前帐号是谁 who 查看当前有哪些人在使用 pwd 当前的工作目录 目录结构 目录结构就是一颗多叉树的样子 路径 我们从 / 目录开始&#xff0c;定位一个叶子文件的…