一、操作系统的概念
操作系统(英语:Operating System,缩写:OS)是一组主管并控制计算机操作、运用和运行硬件、软件资源和提供公共服务来组织用户交互的相互关联的系统软件程序。根据运行的环境,操作系统可以分为桌面操作系统,手机操作系统,服务器操作系统,嵌入式操作系统等。
二、操作系统的分类
操作系统(英语:Operating System,缩写:OS)是一组主管并控制计算机操作、运用和运行硬件、软件资源和提供公共服务来组织用户交互的相互关联的系统软件程序。根据运行的环境,操作系统可以分为桌面操作系统,手机操作系统,服务器操作系统,嵌入式操作系统等。根据服务场景和技术特性,主要分为以下三类:
-
批处理操作系统
指将用户提交的多个作业(任务)按批次自动连续处理的系统。其核心是通过 “多道程序技术” 让内存同时存放多个作业,CPU 在作业间切换以提高资源利用率,用户无需干预作业执行,仅在完成后获取结果。分为单道批处理(内存仅一道作业)和多道批处理(内存多道作业并行调度)。 -
分时操作系统
采用 “时间片轮转” 机制,将 CPU 时间分割为多个短时间片,多个用户通过终端轮流占用 CPU,实现 “同时” 交互操作。用户可实时输入命令并获取响应,具有多路性(多用户共享)、交互性(实时反馈)、独立性(用户互不干扰)和及时性(短响应时间)的特征。 -
实时操作系统
能在严格时间约束内响应外部事件并完成处理的系统,核心目标是 “时效性” 和 “可靠性”。根据时间约束严格程度,分为硬实时系统(必须在绝对时限内完成,如导弹制导)和软实时系统(允许偶尔超时,如视频播放)。
批处理操作系统:工厂流水线
像食品加工厂的流水线 —— 工人把一堆原料(作业)放进生产线,机器自动按顺序清洗、加工、包装,全程无需人工干预,最后批量产出成品。优点是效率高,适合重复、无需修改的任务;缺点是一旦启动,中途不能停下改配方(用户无法交互)。分时操作系统:咖啡厅服务员
好比咖啡厅里的服务员,同时照顾多位客人(用户):给 A 点单、给 B 送咖啡、帮 C 续水,每次只服务一个人一小会儿(时间片),但切换速度极快,客人感觉自己被 “专属服务”。核心是 “随叫随到”(交互性),适合需要随时调整需求的场景(如办公、聊天)。实时操作系统:急诊室医生
类似医院急诊室 —— 病人(外部事件)来了必须立刻处理,且有严格时间限制:心脏骤停的病人需在 4 分钟内除颤(硬实时),轻微外伤可稍等片刻(软实时)。医生(系统)的首要任务是 “不超时”,可靠性远高于效率。
对比维度 批处理操作系统 分时操作系统 实时操作系统 核心目标 提高资源利用率、增加吞吐量 支持多用户实时交互 满足严格时间约束,保证可靠性 用户交互 无(提交后等待结果) 强(实时输入 / 反馈) 有限(多为预设事件响应) CPU 调度方式 多道程序交替运行(无固定时间片) 时间片轮转(固定短时间片) 优先级调度(紧急任务优先) 典型应用 银行对账、大数据批量处理 个人电脑、服务器终端 自动驾驶、工业控制、医疗设备 时间约束 无严格限制 响应时间较短(如秒级) 严格时限(如毫秒 / 微秒级) 资源利用率 高(CPU / 内存持续占用) 中等(切换任务有开销) 按需分配(优先保障关键任务)
三、为什么需要使用系统
当我们进入嵌入式这个领域的时候, 往往首先接触的都是单片机编程, 单片机编程又首选 51 单片机 来入门。 这里面说的单片机编程通常都是指裸机编程,即不加入任何 RTOS(Real Time Operation System 实时操作系统) 的程序。 常用的 RTOS 有国外的 FreeRTOS、μC/OS、 RTX 和国内的 RT-Thread、 Huawei LiteOS 和 AliOS-Things 等, 其中尤以国外开源且免费的 FreeRTOS 的市场占有率 最高。
在裸机系统中,所有的程序基本都是自己写的, 所有的操作都是在一个无限的大循环里面实现。现实 生活中的很多中小型的电子产品用的都是裸机系统, 而且也能够满足需求。但是为什么还要学习 RTOS 编程,偏偏还要整个操作系统进来。一是项目需要,随着产品要实现的功能越来越多,单纯的 裸机系统已经不能够完美地解决问题,反而会使编程变得更加复杂,如果想降低编程的难度, 我们可以考虑引入 RTOS 实现多线程管理, 这是使用RTOS 的最大优势。二是学习的需要,必须学习更高级 的东西,实现更好的职业规划,为将来走向人生巅峰迎娶白富美做准备!
四、移植RT_thread操作系统
1.源码的获取步骤
访问 RT-Thread 官方网站
打开浏览器,在地址栏输入 RT-Thread 官方网站的网址:https://www.rt-thread.org/ ,然后按下回车键进入官网首页。
进入下载页面
在官网页面,点击资源,在最下面找到下载。进入下载界面后点击源代码。
选择合适的版本
在下载页面,会展示不同版本的 RT-Thread 源码。可能会有稳定版、开发版等,根据自己的需求进行选择。比如,如果是用于生产项目,建议选择稳定版;如果是进行新功能测试或开发,可以选择开发版 。
下载源码
- 通过 Git 获取(适合熟悉 Git 工具的开发者):
在下载页面找到 Git 仓库地址(例如,RT-Thread 的 Gitee 仓库地址为https://gitee.com/rtthread/rt-thread ,GitHub 仓库地址为https://github.com/RT-Thread/rt-thread )。
打开命令行工具(在 Windows 系统中可以使用 Git Bash,在 Linux 或 macOS 系统中使用自带的终端),使用git clone
命令克隆仓库。例如,要克隆 Gitee 上的仓库,可以在命令行输入git clone https://gitee.com/rtthread/rt-thread.git
,然后回车,等待源码下载到本地指定目录。- 通过压缩包下载:
在下载页面中,一般会提供源码压缩包下载选项(常见格式有.zip
或.tar.gz
)。点击对应的下载链接,浏览器会开始下载压缩包文件。下载完成后,在下载目录找到压缩包,使用解压工具(如 Windows 系统的 WinRAR、7-Zip,Linux 系统的tar
命令等)解压到指定目录。
2. 文件介绍
获取到的源码解压后如下:
文件夹
- bsp(Board Support Package,板级支持包)
- 作用:包含针对不同硬件开发板的配置文件和驱动程序。它负责初始化硬件设备,如时钟、GPIO、串口、SPI 等,使得 RT - Thread 操作系统能够在特定的硬件平台上运行。
- 举例:在 STM32 开发板上,bsp 文件夹下会有 STM32 芯片初始化代码、时钟配置代码,以及各个外设的驱动代码。开发者可以根据自己使用的具体开发板型号,在该文件夹中找到对应的配置,也可以对其进行修改和扩展以适配自己的硬件设计。
- components(组件)
- 作用:存放 RT - Thread 丰富的软件组件,这些组件为系统增加了各种功能,如文件系统(FinSH 命令行、FatFS 等)、网络协议栈(LwIP 等)、图形界面(LittlevGL 等) 。
- 举例:当需要在 RT - Thread 系统中实现文件管理功能时,就可以使用 components 文件夹中的 FatFS 文件系统组件;如果要开发具有网络通信功能的设备,就可以使用 LwIP 网络协议栈组件。
- include(头文件)
- 作用:包含 RT - Thread 操作系统及各个组件的头文件。这些头文件定义了各种数据结构、函数原型、宏定义等,方便开发者在自己的应用程序中引用,以调用 RT - Thread 提供的功能。
- 举例:在编写 RT - Thread 应用程序时,要使用线程相关的函数,就需要包含
rtthread.h
头文件,该头文件位于 include 文件夹下,它定义了线程控制块、线程创建函数等相关的内容。- libcpu(CPU 相关库)
- 作用:主要存放与 CPU 架构相关的代码,如中断处理、任务切换、CPU 寄存器操作等底层代码。它实现了 RT - Thread 操作系统在不同 CPU 架构上的移植接口。
- 举例:对于 ARM Cortex - M 系列 CPU,libcpu 文件夹下会有针对该系列 CPU 的中断向量表定义、任务切换汇编代码等,使得 RT - Thread 能够在 ARM Cortex - M 芯片上高效运行。
- src(操作系统核心源码)
- 作用:存放 RT - Thread 操作系统的核心源代码,包括线程管理、内存管理、信号量、消息队列、事件集等内核对象的实现代码。
- 举例:线程管理的实现代码负责线程的创建、删除、挂起、恢复等操作;内存管理代码实现了 RT - Thread 系统的内存分配和释放机制,如静态内存池和动态内存堆的管理。
文件
- AUTHORS
- 作用:记录参与 RT - Thread 开发的作者信息,包括姓名、邮箱等。
- 举例:当开发者想要了解 RT - Thread 的开发团队,或者在使用过程中遇到问题需要联系特定作者时,可以查看该文件获取相关信息。
- COPYING
- 作用:主要包含 RT - Thread 的版权声明和许可协议,明确了该开源软件的使用、分发、修改等相关法律条款。
- 举例:开发者在基于 RT - Thread 进行商业开发或者开源项目分发时,需要遵循 COPYING 文件中规定的许可协议,以确保合法合规。
- License
- 作用:和 COPYING 文件类似,进一步明确 RT - Thread 的开源许可证信息,如开源协议类型(通常是 Apache 2.0 等)。
- 举例:通过查看 License 文件,开发者可以清楚地知道自己在使用 RT - Thread 时的权利和义务,避免因版权问题产生纠纷。
- README
- 作用:是对 RT - Thread 源码的简要说明文档,通常会介绍 RT - Thread 的基本特性、目录结构、编译方法、使用说明等重要信息。
- 举例:对于初次接触 RT - Thread 的开发者,通过阅读 README 文件,可以快速了解 RT - Thread 的基本情况,以及如何开始搭建开发环境和进行代码编译。
- rt - thread.rttthread.pdsc
- 作用:这是 RT - Thread 的软件包描述文件,用于描述 RT - Thread 软件包的属性,如名称、版本、依赖关系等信息,在使用 RT - Thread Studio 等开发工具进行软件包管理时会用到。
- 举例:当使用 RT - Thread Studio 创建项目并添加 RT - Thread 软件包时,工具会读取该文件来获取软件包的相关信息,以便正确地配置和管理项目中的软件包。
3. 移植
将下载的源码文件夹复制到项目中。
将源码文件夹的bsp文件夹中的下面两个文件复制到工程文件夹的user下:
点击keil,打开工程:
按照下面的步骤添加文件:
全选src的所有文件并添加:(当第4步Add点击之后,左边5会出现)
然后再向rtt_port添加,右边的路径在源码下的libcpu下的arm下的cortex-m3,文件类型选择全部,并添加以下两个文件:
点击ok之后,工程之中新出现两个分组:
将rt_thread的只读属性去掉(把下图中的只读选项去掉):
添加board:
右键USER添加并选择上面步骤复制的board.c并去掉只读属性。
添加头文件:
选择finish:
选择include:
选择libc库:
点击ok:
点击编译,左侧文件出现加号说明添加成功,下一步就是修改出现的报错:
以下是错误的修改步骤:
错误一:
..\USER\rtconfig.h(6): error: #5: cannot open source input file "RTE_Components.h": No such file or directory#include "RTE_Components.h" ..\rt_thread\src\clock.c: 0 warnings, 1 error
解决方案:注释掉此头文件
错误二:重定义
.\Objects\STM32F103ZET6.axf: Error: L6200E: Symbol HardFault_Handler multiply defined (by context_rvds.o and stm32f10x_it.o). .\Objects\STM32F103ZET6.axf: Error: L6200E: Symbol PendSV_Handler multiply defined (by context_rvds.o and stm32f10x_it.o). .\Objects\STM32F103ZET6.axf: Error: L6200E: Symbol SysTick_Handler multiply defined (by board.o and stm32f10x_it.o).
由于RTOS操作系统内部自带定时器,所以可以把裸机中使用的定时器函数注释掉:
至此为止,编译通过。
将rtconfig.h中的RT_THREAD_PRIORITY_MAX值从8改为32:
将RT_TICK_PER_SECOND从100改为1000:
将RT_MAIN_THREAD_STACK_SIZE从256改为512:
取消注释RT_USING_HEAP:
将board.c中的下面这部分的代码注释掉:
同时也注释掉下面这部分:
自定义的驱动程序初始化都在这里:
并在当前board.c文件添加这些包含这些驱动函数的头文件board.h,具体间自己的目录结构,以下为我的:
再次编译,没有错误了。
4. 验证工程的可用性
使用rt_thread_create和rt_thread_startup创建线程,线程的执行函数自定义,下面只是示例代码:
main.c:
#include "stm32f10x.h"#include "board.h"
#include "rtthread.h"
#include "string.h"uint8_t key_key0_state = KEY_DOWN;
uint8_t key_key1_state = KEY_DOWN;
uint8_t key_wkup_state = KEY_DOWN;void led1_thread(void *parg);
rt_thread_t led1_ret;int main(void) {led1_ret=rt_thread_create("led1",led1_thread,RT_NULL,512,7,20);if(led1_ret!=RT_NULL){rt_thread_startup(led1_ret);}while(1){}//return 0;
}void led1_thread(void *parg)
{while(1){LED1ON();rt_thread_delay(1000);rt_kprintf("LED1ON\r\n");LED1OFF(); rt_thread_delay(1000); /* 延时500个tick */ rt_kprintf("LED1OFF\r\n");}
}
然后将编译好的代码烧写进单片机中,然后就会发现led灯交替亮灭。至此为止,我们就辛辛苦苦用RTOS点亮了一盏灯!!!
虽然感觉有点亏吧,但是这仅仅是刚刚开始,随着我们的项目越来越丰富,RTOS的作用也就越来越大。
0voice · GitHub