文章目录
- 1. 嵌入式系统软件基础知识
- 1.1 嵌入式软件分类
- 1.2 嵌入式系统初始化
- 1.3 无操作系统支持的嵌入式软件体系结构
- 1.4 有操作系统支持的嵌入式软件体系结构
- 1.5 嵌入式支撑软件
- 2. 嵌入式操作系统基础知识
- 2.1 嵌入式操作系统基本概念
- 2.2 处理器管理
- 2.2.1 多道程序
- 2.2.2 分区、进程、线程、任务的概念
- 2.2.3 任务管理
- 2.2.4 任务调度
- 2.2.5 优先级反转问题
- 2.2.6 任务间通信
- 2.2.7 同步与互斥
- 2.2.8 高可靠性系统的分区调度与通信
- 2.3 存储管理
- 2.3.1 存储管理方式
- 2.3.2 分区存储管理
- 2.3.3 地址重定位
- 2.3.4 页式存储管理
- 2.3.5 虚拟存储技术
- 2.4 设备管理
- 2.4.1 物理设备、逻辑设备、虚拟设备
- 2.4.2 设备分类
- 2.4.3 设备管理方式
- 2.4.4 设备驱动程序
- 2.5 文件系统
- 2.5.1 文件与目录
- 2.5.2 文件结构与组织
- 2.5.3 存取方法与存取控制
- 2.5.4 常见嵌入式文件系统
- 2.5.5 网络文件系统
- 2.6 操作系统移植
1. 嵌入式系统软件基础知识
1.1 嵌入式软件分类
嵌入式软件按照功能和作用可以分为以下几个类别:
- 系统软件:如引导程序(Bootloader)、板级支持包(BSP)等,负责系统的初始化和底层驱动。
- 支撑软件:包括文件系统、数据库、图形界面(GUI)等,为应用提供运行环境。
- 中间件:在操作系统和应用之间提供服务,如通信协议栈、分布式对象系统等。
- 可配置组件:可以根据需求进行动态配置的模块,提升系统的灵活性。
- 应用软件:直接面向用户或实现具体功能的程序,如控制逻辑、数据处理等。
1.2 嵌入式系统初始化
嵌入式系统的启动过程是整个系统运行的基础,主要包括以下两个关键部分:
-
系统引导(Bootloader)
Bootloader(系统引导程序)是嵌入式系统启动的第一段代码,主要完成三项核心工作:1)硬件初始化(时钟、内存、外设等);2)加载操作系统镜像到内存;3)跳转到OS入口。典型代表如U-Boot支持多种架构,提供网络、存储等扩展功能。其执行流程为:ROM代码→Bootloader阶段1(低级初始化)→阶段2(高级功能)→启动内核。 -
板级支持包(BSP)
BSP(板级支持包)是硬件与操作系统间的适配层,包含:1)处理器特定代码(中断控制器、定时器驱动);2)板级硬件抽象(GPIO映射、存储器配置);3)设备驱动框架。开发时需根据具体硬件修改BSP,如调整内存映射表、实现定制外设驱动等。
Bootloader与BSP的关系是:Bootloader依赖BSP完成基础硬件初始化,而OS通过BSP管理硬件资源。两者共同确保系统从启动到运行的平滑过渡。
系统启动流程如下图所示:
1.3 无操作系统支持的嵌入式软件体系结构
在一些资源受限或对实时性要求极高的系统中,可能不使用操作系统。这类系统通常采用轮询机制或中断服务程序来实现任务调度,具有结构简单、响应迅速的特点,但缺乏多任务管理和资源调度能力。
1.4 有操作系统支持的嵌入式软件体系结构
引入嵌入式操作系统后,系统具备了多任务管理、内存管理、设备驱动抽象、文件系统支持等功能,提高了系统的稳定性、可移植性和可扩展性。典型的嵌入式操作系统包括 FreeRTOS、VxWorks、μC/OS、Linux 等。
1.5 嵌入式支撑软件
嵌入式支撑软件为应用开发提供基础服务,主要包括:
- 嵌入式文件系统:如 FAT、JFFS、YAFFS、EXT 等,适用于不同存储介质和应用场景。
- 嵌入式数据库:轻量级数据库如 SQLite、TinyDB,用于本地数据存储与查询。
- 分布式对象系统:如 CORBA、DDS,支持跨平台通信与数据共享。
- 图形用户界面(GUI):如 Qt、MiniGUI、LVGL,用于构建可视化交互界面。
2. 嵌入式操作系统基础知识
2.1 嵌入式操作系统基本概念
嵌入式操作系统(Embedded OS)是专为嵌入式系统设计的操作系统,具有体积小、功耗低、实时性强、可裁剪等特点。它通常运行在资源有限的处理器上,如 ARM、MIPS、RISC-V 等架构。
2.2 处理器管理
2.2.1 多道程序
多道程序是指允许多个程序同时驻留在内存中,通过分时共享 CPU 资源来提高系统利用率。核心特点为:
- 并发执行:多个程序交替占用 CPU,宏观上"同时"运行。
- 资源复用:CPU、内存、I/O 设备等资源被多个程序共享。
- 调度机制:采用时间片轮转(Round-Robin)或优先级调度决定哪个程序先执行。
2.2.2 分区、进程、线程、任务的概念
- 分区:将系统资源(内存、CPU、外设等)划分为多个独立区域,用于隔离不同功能模块或应用程序。防止一个模块崩溃影响整个系统(如微内核架构)。关键任务独占分区,避免资源竞争。
- 进程:程序的执行实例,拥有独立的地址空间、文件描述符等资源。进程间通常不直接共享内存(需 IPC 机制,如管道、消息队列)。
- 线程:进程内的执行单元,共享同一进程的资源(如内存、文件句柄)。线程间可直接读写共享数据(需同步机制,如互斥锁)。
- 任务(Task):通常指实时调度的基本单位,内存占用极简(可能无MMU支持)。
2.2.3 任务管理
由操作系统内核提供完整的管理机制,包括:
- 创建:分配任务控制块(TCB),设置优先级和栈空间
- 调度:基于优先级或时间片的就绪队列管理
- 同步:通过信号量、事件标志等机制实现任务协调
- 状态控制:支持挂起、恢复、删除等操作
- 资源回收:任务终止时自动释放占用的系统资源
2.2.4 任务调度
抢占式调度:
- 高优先级任务可以随时中断低优先级任务的执行,例如任务A在运行过程中,突然来了任务B,如果任务B优先级更高,则CPU会挂起任务A先直接抢占执行任务B。
非抢占式调度:
- 任务一旦获得CPU就会一直运行,直到完成或主动放弃CPU,例如任务A在运行过程中,突然来了任务B,即使任务B优先级更高,CPU也会先执行完任务A,等到任务A主动放弃CPU后才会执行优先级更高的任务B。
2.2.5 优先级反转问题
优先级反转是指高优先级任务被迫等待低优先级任务的情况,通常发生在资源共享时。
这张图展示了在没有优先级继承和有优先级继承两种情况下,优先级抢占对系统行为的影响。我们可以通过对比这两种情况来理解优先级继承的作用。
没有优先级继承的情况
-
初始状态:
- 低优先级线程(绿色)获得锁并开始执行。
-
高优先级线程介入:
- 高优先级线程(红色)尝试获取锁,但因为锁被低优先级线程持有,所以它进入等待状态。
- 此时,中等优先级线程(橙色)可以抢占低优先级线程的CPU资源,并开始执行。
- 低优先级线程被挂起,直到中等优先级线程释放CPU资源。
-
锁释放与重新获取:
- 当低优先级线程再次获得CPU资源并释放锁后,高优先级线程才能继续执行。
- 在此过程中,高优先级线程经历了不必要的延迟,这种现象被称为“优先级反转”。
有优先级继承的情况
-
初始状态:
- 低优先级线程(绿色)获得锁并开始执行。
-
高优先级线程介入:
- 高优先级线程(红色)尝试获取锁,但因为锁被低优先级线程持有,所以它进入等待状态。
- 此时,低优先级线程的优先级被临时提升到高优先级线程的级别,以防止中等优先级线程(橙色)抢占其CPU资源。
- 因此,低优先级线程能够尽快完成其任务并释放锁。
-
锁释放与恢复优先级:
- 当低优先级线程释放锁后,其优先级恢复到原来的低优先级。
- 高优先级线程立即获得锁并继续执行,避免了不必要的延迟。
总结:通过优先级继承机制,当一个低优先级线程持有某个资源(如锁),而高优先级线程需要访问该资源时,低优先级线程的优先级会被临时提升,以确保它能尽快完成任务并释放资源。这样可以避免优先级反转问题,保证高优先级线程能够及时得到所需的资源,从而提高系统的实时性和响应性。
2.2.6 任务间通信
- 共享内存:多个任务直接访问同一块内存区域进行数据交换,需配合同步机制避免冲突。
- 消息队列:内核维护的FIFO缓冲区,任务通过发送/接收结构化消息实现异步通信。
- 邮箱:简化版消息队列,通常只允许存储单条消息,提供消息通知机制。
- 管道:基于文件描述符的字节流通信方式,支持单向数据流动,常用于父子进程通信。
- 信号量:用于控制资源访问的计数器,实现任务间的同步/互斥,分二进制和计数型两种。
2.2.7 同步与互斥
-
互斥:在多线程或多进程环境中,为了防止同时访问共享资源而引起的数据不一致问题,可以使用信号量实现互斥访问。这通常通过一个初始值为1的二进制信号量来完成,也称为互斥锁。任何需要访问共享资源的线程必须首先执行P操作以获得锁,访问结束后执行V操作释放锁。
-
同步:信号量也可以用来控制进程或线程之间的执行顺序,确保某些事件按照预定的顺序发生。例如,在生产者-消费者问题中,可以通过两个信号量分别控制缓冲区的空位数量和满位数量,从而协调生产者和消费者的行为。
在同步与互斥机制中,"PV操作"是一种经典的信号量(Semaphore)操作,用于解决进程或线程间的同步和互斥问题。
“P操作"和"V操作"这两个术语来源于荷兰语:“Proberen”(尝试)和"Verhogen”(增加),它们是由计算机科学家艾兹赫尔·戴克斯特拉(Edsger Dijkstra)提出。
-
P操作(Proberen, 减少):通常被理解为“等待”或“减量”操作。当一个进程执行P操作时,如果当前信号量的值大于0,该值将减1,并且进程可以继续执行;如果信号量的值0,表示没有可用资源或者需要等待的条件尚未满足,这时进程会被阻塞,直到有其他进程执行了V操作使得信号量的值变为正数。
-
V操作(Verhogen, 增加):通常被理解为“释放”或“增量”操作。当一个进程执行V操作时,它会增加信号量的值。如果此时有其他进程正在等待这个信号量(即之前执行P操作后被阻塞的进程),那么其中一个(或者多个,取决于信号量的类型是二进制还是计数)将会被唤醒并允许继续执行。
2.2.8 高可靠性系统的分区调度与通信
采用分区机制(如 ARINC 653 标准),将系统划分为多个隔离的时间与空间分区,保证关键任务的实时性和安全性。
2.3 存储管理
2.3.1 存储管理方式
- 连续分配:程序装入内存时分配连续的物理地址空间,包括单一连续分配和固定/可变分区分配。
- 优势:实现简单,访问速度快(无地址转换开销),适合静态程序。
- 劣势:内存碎片化严重(外部碎片),内存利用率低,程序大小受分区限制
- 离散分配:将程序分散存储在非连续的物理内存中,通过分页/分段/段页式机制实现地址映射。
- 优势:提高内存利用率(减少碎片),支持动态加载,程序大小不受物理内存限制
- 劣势:实现复杂,需要硬件支持(MMU),存在地址转换开销(页表/段表查询)
2.3.2 分区存储管理
- 固定分区:静态划分内存区域。
- 可变分区:根据任务大小动态划分。
- 内存保护:防止非法访问其他任务的内存空间。
2.3.3 地址重定位
地址重定位是指将程序中的逻辑地址(相对地址)转换为内存中实际物理地址的过程,这是实现多道程序运行的关键技术。
在程序编译时生成的逻辑地址需要通过静态重定位(程序装入时一次性转换)或动态重定位(运行时通过内存管理单元MMU实时转换)映射为物理地址,其中动态重定位是现代操作系统的主流方式,它通过页表、段表等映射机制实现地址转换,既支持虚拟内存管理,又能实现内存访问保护和共享。
这种机制的优势在于提高了内存利用率,使程序可以分散存储在非连续的物理内存中,同时支持内存保护和进程隔离;但需要硬件MMU的支持,且地址转换会带来一定的性能开销。
2.3.4 页式存储管理
将内存划分为固定大小的“页”,便于管理与调度。
2.3.5 虚拟存储技术
-
程序局部性原理
- 时间局部性:最近被访问的数据很可能短期内再次被访问
- 空间局部性:程序倾向于访问邻近当前地址的内存区域
-
虚拟页式存储管理
将进程地址空间划分为固定大小的页(Page),通过页表实现虚拟地址到物理地址的映射,支持按需调页和页面置换 -
页面置换算法
- FIFO:替换最先进入内存的页面,实现简单但效率较低
- LRU(最近最少使用):替换最长时间未被访问的页面,效果较好但实现复杂
- OPT(最优置换):理论最优算法,替换未来最长时间不被访问的页面(实际无法实现)
2.4 设备管理
2.4.1 物理设备、逻辑设备、虚拟设备
- 物理设备:实际存在的硬件。
- 逻辑设备:操作系统提供的抽象接口。
- 虚拟设备:通过软件模拟的设备。
2.4.2 设备分类
- 字符设备:按字节顺序访问(如串口)。
- 块设备:按固定大小的数据块读写(如硬盘)。
- 网络设备:处理网络通信。
2.4.3 设备管理方式
- 设备文件:Linux系统将硬件设备抽象为/dev目录下的特殊文件,通过标准文件操作接口(open/read/write)访问设备。
- 设备无关性:操作系统提供统一的设备访问接口,使应用程序无需关心具体设备的硬件特性差异。
- 中断处理:当外设完成I/O操作时通过中断信号通知CPU,由中断服务程序进行即时响应和处理。
- 缓冲技术:在内存中建立数据缓冲区,用于平滑CPU与I/O设备间的速度差异,减少中断次数。
- 假脱机技术(SPOOLing):将独占设备虚拟为多个共享设备,典型应用如打印任务队列管理。
2.4.4 设备驱动程序
驱动程序是操作系统与硬件之间的桥梁,负责控制和管理设备的行为。
2.5 文件系统
2.5.1 文件与目录
- 文件是信息的基本单位。
- 目录用于组织文件结构。
2.5.2 文件结构与组织
- 连续结构、链式结构、索引结构等。
2.5.3 存取方法与存取控制
- 随机访问、顺序访问。
- 权限控制、加密等。
2.5.4 常见嵌入式文件系统
- FAT:兼容性好,适合小型存储。
- RAMFS:基于内存的文件系统,速度快。
- ROMFS:只读文件系统,适合固件。
- JFFS/JFFS2:适用于 NOR Flash。
- YAFFS/YAFFS2:针对 NAND Flash 的优化。
- EXT4:Linux 常用,功能丰富。
2.5.5 网络文件系统
- NFS(Network File System):远程挂载文件系统。
2.6 操作系统移植
操作系统移植是指将操作系统适配到新的硬件平台,主要包括以下步骤:
- 硬件配置分析
- BSP 移植:修改底层硬件初始化代码。
- 驱动移植:编写或适配外设驱动。
- 系统配置:选择内核模块、配置参数。
- 交叉编译:在宿主机上编译目标平台的代码。
- 部署与测试:烧录镜像、验证功能。
嵌入式系统的开发是一个软硬件协同的过程,理解嵌入式软件体系结构与操作系统的工作机制是构建高效、稳定、可靠系统的关键。