嵌入式系统操作系统:ERIKA与嵌入式Linux深度解析

1. SRP协议概述

SRP(Stack Resource Policy)协议在资源调度方面有着独特的优势。通过图4.13的示例可以看出,它与PCP(Priority Ceiling Protocol)协议存在明显差异。在时间t1和t4时,由于抢占级别不高于上限,SRP不会发生抢占。总体而言,SRP的抢占次数明显少于PCP,这一特性使得SRP成为一种受欢迎的协议。

SRP被称为堆栈资源策略,因为作业不会被优先级较低的作业阻塞,并且只有在作业完成时才能恢复执行。因此,同一级别li的作业可以共享堆栈空间。当同一级别有多个作业时,可以节省大量的空间。此外,SRP还没有死锁问题。

2. ERIKA实时内核

许多嵌入式系统,如汽车系统和家用电器,需要将整个应用程序托管在小型微控制器上。因此,这些系统的固件提供的操作系统服务必须限制在一组最小的功能范围内,以支持周期性和非周期性作业的多线程执行,并支持共享资源,以避免优先级反转现象。

OSEK/VDX联盟在20世纪90年代将这些需求进行了规范化,定义了多线程实时操作系统的最小服务,允许在8位微控制器上实现1 - 10千字节的代码占用空间。AUTOSAR联盟最近扩展了OSEK/VDX API,提供了时间保护、时间触发系统的调度表以及内存保护等增强功能。

下面以开源的ERIKA Enterprise实时内核为例,介绍其主要特点和要求:
- 静态定义内核对象 :OSEK内核与其他操作系统的第一个区别是,所有内核对象在编译时都是静态定义的。大多数此类系统不支持动态内存分配和作业的动态创建。为了帮助用户配置系统,OSEK/VDX标准提供了一种名为OIL的配置语言,用于指定应用程序中必须实例化的对象。当应用程序编译时,OIL编译器会生成操作系统数据结构,分配所需的确切内存量。这种方法允许只分配应用程序真正需要的数据,并将其存储在闪存中(在大多数微控制器上,闪存比RAM内存更便宜)。
- 支持堆栈共享 :OSEK/VDX系统的第二个特点是支持堆栈共享。由于小型微控制器上的RAM内存非常昂贵,因此提供堆栈共享的可能性。堆栈共享系统的实现与代码的编写方式有关。

在传统的实时系统中,周期性任务的典型实现结构如下:

task(x) {
    int local;
    initialization();
    for (;;) {
        do_instance();
        end_instance();
    }
}

这种方案的特点是一个无限循环,包含一个周期性任务的实例(作业),该实例以阻塞原语(end_instance())结束,其作用是阻塞任务,直到下一次激活。在这种情况下,任务即使在等待时间也始终存在于堆栈中,因此堆栈无法共享,必须为每个任务分配单独的堆栈空间。

OSEK/VDX标准还支持基本任务,其实现方式更类似于函数,如下所示:

int local;
task x() {
    do_instance();
}
System_initialization() {
    initialization();
}

与扩展任务相比,基本任务在不同实例之间必须维护的持久状态不存储在堆栈中,而是存储在全局变量中。初始化部分也移到了系统初始化中,因为任务不是动态创建的,而是从一开始就存在。最后,不需要同步原语来阻塞任务直到下一个周期,因为任务在每次新实例开始时都会被激活。此外,任务不能调用任何阻塞原语,因此它要么被更高优先级的任务抢占,要么执行到完成。这样,任务的行为就像一个函数,在堆栈上分配一个帧,运行,然后清理该帧。因此,任务在两次执行之间不占用堆栈空间,允许系统中的所有基本任务共享一个堆栈,从而减少了为此目的使用的总体RAM内存。

  • 任务管理 :OSEK/VDX内核支持固定优先级调度,并使用立即优先级上限来避免优先级反转问题。通过在OIL配置文件中指定每个任务的资源使用情况来支持立即优先级上限的使用。OIL编译器根据每个任务在OIL文件中声明的资源使用情况计算每个任务的资源上限。

OSEK/VDX系统还支持非抢占式调度和抢占阈值,以限制总体堆栈使用。主要思想是限制任务之间的抢占可以减少同时分配在系统堆栈上的任务数量,进一步减少所需的总体RAM量。需要注意的是,减少抢占可能会降低任务集的可调度性,因此必须在系统可调度性和系统中使用的总体RAM内存之间权衡抢占程度。

  • 可扩展性 :为小型微控制器设计的操作系统的另一个要求是可扩展性,即支持为较小的代码占用空间实现提供简化版本的API。在大规模生产系统中,代码占用空间会显著影响总体成本。在这种情况下,可扩展性通过一致性类的概念来提供,一致性类定义了操作系统API的特定子集。一致性类还伴随着它们之间的升级路径,最终目标是支持具有减少代码占用空间的标准的部分实现。

OSEK/VDX标准(以及ERIKA Enterprise)支持的一致性类如下:
| 一致性类 | 描述 |
| ---- | ---- |
| BCC1 | 最小的一致性类,支持最少八个不同优先级的任务和一个共享资源。 |
| BCC2 | 与BCC1相比,该一致性类增加了在同一优先级上有多个任务的可能性。每个任务可以有挂起的激活,即操作系统记录已激活但尚未执行的实例数量。 |
| ECC1 | 与BCC1相比,该一致性类增加了具有可以等待事件出现的扩展任务的可能性。 |
| ECC2 | 该一致性类同时增加了多个激活和扩展任务。 |

ERIKA Enterprise进一步扩展了这些一致性类,提供了以下两个一致性类:
- EDF :该一致性类不使用固定优先级调度器,而是使用为小型微控制器实现优化的最早截止日期优先(EDF)调度器。
- FRSH :该一致性类通过提供基于IRIS调度算法的资源预留调度器来扩展EDF调度器类。

  • 中断控制API :OSEK/VDX系统的另一个有趣特性是提供了一个用于控制中断的API。这与POSIX类系统有很大不同,在POSIX类系统中,中断是操作系统的专属领域,不导出到操作系统API。原因是在小型微控制器上,用户通常希望直接控制中断优先级,因此提供一种标准的方法来处理中断禁用/启用非常重要。

OSEK/VDX标准指定了两种类型的中断服务例程(ISR):
- 类别1 :更简单、更快,在ISR结束时不调用调度器。
- 类别2 :该ISR可以调用一些改变调度行为的原语。ISR结束是一个重新调度点。ISR1的优先级始终高于ISR2。

  • 精细调整代码占用空间 :OSEK/VDX内核的一个重要特性是可以通过从生产版本中删除错误检查代码以及定义在特定事件发生时由系统调用的钩子来精细调整代码占用空间。这些功能允许对应用程序的代码占用空间进行精细调整,调试时代码占用空间会更大(更安全),而在生产时,当大多数错误被发现并从代码中移除后,代码占用空间会更小。

为了支持更好的调试体验,OSEK/VDX标准定义了一种名为ORTI的文本语言,用于描述操作系统的各种对象的分配位置。ORTI文件通常由OIL编译器生成,供调试器打印有关系统中定义的操作系统对象的详细信息(例如,调试器可以打印应用程序中任务的列表及其当前状态)。

所有由OSEK/VDX标准定义的功能都已在开源的ERIKA Enterprise内核中实现,适用于一组嵌入式微控制器,最终的目标代码占用空间在1到5千字节之间。ERIKA Enterprise还实现了其他功能,如EDF调度器,提供了一个开放且免费的操作系统,可用于学习、测试和实现工业和教育目的的实际应用程序。

3. 嵌入式Linux概述

随着嵌入式系统功能需求的增加,如互联网连接(特别是物联网)或复杂的图形显示,需要在典型嵌入式系统的简单操作系统中添加大量软件。虽然可以通过集成小型互联网协议(IP)网络堆栈等方式为小型嵌入式实时操作系统添加一些功能,但集成多个不同的附加软件组件是一项复杂的任务,可能会导致功能和安全缺陷。

根据摩尔定律,半导体密度呈指数级增长,这使得将具有所需功能的经过充分测试的代码库适应嵌入式环境成为一种不同的方法。Linux已成为许多复杂嵌入式应用程序的首选操作系统,如互联网路由器、GPS卫星导航系统、网络附加存储设备、智能电视机和移动电话等。这些应用程序受益于Linux的易移植性(Linux已被移植到30多种处理器架构,包括流行的嵌入式ARM、MIPS和PowerPC架构)以及系统的开源性质,避免了商业嵌入式操作系统的许可成本。

然而,由于Linux最初设计为服务器和桌面操作系统,将其适应典型的嵌入式环境面临着一些挑战。下面将详细介绍Linux为解决在嵌入式系统中使用时出现的常见问题所提供的解决方案。

4. 嵌入式Linux结构和大小

严格来说,“Linux”一词仅指基于Linux的操作系统的内核。要创建一个完整的、可工作的操作系统,需要在Linux内核之上运行许多其他组件。一个典型的Linux系统配置,包括系统级用户模式组件,如下所示:

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    Hardware --> Architecture-DependentCode[Architecture-Dependent Code]:::process
    Architecture-DependentCode --> SystemCallInterface[System Call Interface]:::process
    SystemCallInterface --> IORelated[I/O Related]:::process
    SystemCallInterface --> ProcessRelated[Process Related]:::process
    IORelated --> KernelModules[Kernel Modules]:::process
    ProcessRelated --> KernelModules
    KernelModules --> DeviceDrivers[Device Drivers]:::process
    KernelModules --> Scheduler[Scheduler]:::process
    KernelModules --> MemoryMgmt[Memory Mgmt.]:::process
    KernelModules --> IPC[IPC]:::process
    DeviceDrivers --> SystemLibraries[System Libraries <br> (e.g. libc)]:::process
    Scheduler --> SystemLibraries
    MemoryMgmt --> SystemLibraries
    IPC --> SystemLibraries
    SystemLibraries --> Applications[Applications]:::process
    SystemLibraries --> FileSystems[File Systems]:::process
    SystemLibraries --> Networking[Networking]:::process
    SystemLibraries --> DeviceSupport[Device Support]:::process

在Linux内核之上,有许多通常是动态链接的库,它们构成了系统级工具和应用程序的基础。Linux中的设备驱动程序通常实现为可加载的内核模块,但也可以进行受限的用户模式硬件访问。

Linux的开源性质允许根据给定应用程序和平台的要求定制内核和其他系统组件。这反过来又导致了一个小型系统,使得Linux可以在内存大小受限的系统中使用。

Unix类系统的一个重要组件是C库,它为文件I/O、进程同步和通信、字符串处理、算术运算和内存管理等提供基本功能。Linux系统中常用的libc变体是GNU libc(glibc)。然而,glibc是为服务器和桌面系统设计的,因此提供的功能远远超过嵌入式应用程序通常所需的功能。基于Linux的Android系统用Bionic(一种源自BSD Unix的libc版本)取代了glibc。Bionic专门设计用于支持以较低时钟速度运行的系统,例如,通过提供定制版本的Pthreads多线程库来有效地支持Android的Dalvik Java VM。Bionic的大小估计约为典型glibc版本的一半。

还有几种明显更小的libc实现,如newlib、musl、uClibc、PDCLib和dietlibc。每种实现都针对特定的用例进行了优化:
| libc版本 | 静态库大小 | 共享库大小 | 最小静态C程序大小 | 最小静态“Hello, World”大小 |
| ---- | ---- | ---- | ---- | ---- |
| musl | 426 kB | 527 kB | 1.8 kB | 13 kB |
| uClibc | 500 kB | 560 kB | 5 kB | 70 kB |
| dietlibc | 120 kB | 185 kB | 0.2 kB | 6 kB |
| glibc | 2.0 MB | 7.9 MB | 662 kB | 662 kB |

除了C库,与操作系统捆绑的实用程序的功能、大小和数量也可以根据应用程序的要求进行调整。这些实用程序在Linux系统中用于控制系统启动、操作和监控,例如挂载文件系统、配置网络接口或复制文件的工具。与glibc一样,典型的Linux系统包含一组适用于大量用例的工具,但其中大多数在嵌入式系统上是不需要的。

BusyBox是传统多样化工具集的替代方案,它是一种软件,在单个可执行文件中提供了许多简化的基本Unix实用程序。它专门为资源非常有限的嵌入式操作系统而创建。BusyBox减少了可执行文件格式带来的开销,并允许在多个应用程序之间共享代码而无需库。有关BusyBox与提供小型用户模式工具集的其他方法的比较,可以参考相关资料。

5. 嵌入式Linux的实时特性

在基于通用操作系统内核的系统中实现实时保证是将操作系统适应嵌入式环境运行时最复杂的挑战之一。一种常见的方法是将Linux内核和所有Linux用户模式进程作为底层RTOS的一个专用任务运行,只有在没有实时任务需要运行时才激活。在Linux中,有几种遵循这种设计模式的竞争方法:
- RTAI(实时应用程序接口) :基于Adeos虚拟机管理程序,它作为Linux内核扩展实现。Adeos允许在同一硬件上同时存在多个优先级域(其中之一是Linux内核本身)。在此基础上,RTAI提供了一个服务API,例如,用于控制中断和系统定时器。
- Xenomai :与RTAI共同开发了几年,但在2005年成为一个独立项目。它基于自己的抽象“核心”RTOS核心,提供实时调度、定时器、内存分配和虚拟文件处理服务。RTAI和Xenomai的目标和实现有所不同,但它们都支持实时驱动模型(RTDM),这是一种统一实时Linux系统中设备驱动程序和相关应用程序开发接口的方法。
- RTLinux :由新墨西哥矿业与技术学院作为一个项目开发,后来在FSMLabs公司商业化,该公司于2007年被Wind River收购。相关产品于2011年停产。RTLinux在产品中的使用存在争议,因为其发起者大力捍卫其知识产权,并为此获得了软件专利。这一专利决定在Linux开发者社区中不受欢迎,导致了上述RTAI和Xenomai项目的衍生。

一种较新的为Linux添加实时功能的方法是SCHED_DEADLINE,这是一种自2014年Linux 3.14版本起集成到内核中的CPU调度策略。它基于最早截止日期优先(EDF)和恒定带宽服务器(CBS)算法,并支持资源预留。SCHED_DEADLINE策略设计为与其他Linux调度策略共存,但它在所有其他策略之前具有优先级,以保证实时属性。

每个在SCHED_DEADLINE调度下的任务τi都与一个运行时预算Ci和一个周期Ti相关联,向内核表明该任务每Ti时间单位需要Ci时间单位,在任何处理器上都是如此。对于实时应用程序,Ti对应于任务后续激活(释放)之间的最小时间间隔,Ci对应于任务每次执行所需的最坏情况执行时间。当向此调度策略添加新任务时,会进行可调度性测试,只有测试成功时任务才会被接受。在调度过程中,当任务试图运行的时间超过预分配的预算时,它会被挂起并推迟到下一个执行周期。这种非工作保守策略是为了保证不同任务之间的时间隔离。因此,在单处理器或分区多处理器系统(任务固定在特定CPU上)中,所有被接受的SCHED_DEADLINE任务都保证在每个长度等于其周期的时间窗口内被调度的总时间等于其预算。

综上所述,无论是ERIKA Enterprise实时内核还是嵌入式Linux,都在各自的领域为嵌入式系统的发展提供了有力的支持。它们通过不同的方式满足了嵌入式系统对资源管理、实时性等方面的需求,为嵌入式系统的应用和发展奠定了坚实的基础。

嵌入式系统操作系统:ERIKA与嵌入式Linux深度解析

6. ERIKA与嵌入式Linux的对比

在了解了 ERIKA 和嵌入式 Linux 的特性后,我们可以对它们进行一些对比,以便在实际应用中做出更合适的选择。

特性 ERIKA 嵌入式 Linux
代码占用空间 小,最终目标代码占用空间在 1 到 5 千字节之间 可定制,通过选择合适的组件和库可实现较小的占用空间,但整体可能比 ERIKA 大
实时性 通过固定优先级调度、立即优先级上限等机制保证实时性,支持多种一致性类扩展实时调度策略 有多种方法实现实时性,如 RTAI、Xenomai 和 SCHED_DEADLINE 等,但实现相对复杂
可扩展性 通过一致性类的概念支持可扩展性,可根据需求选择不同的子集 开源性质允许根据应用和平台要求定制内核和系统组件
内存管理 支持堆栈共享,减少 RAM 内存使用 有多种 C 库可供选择,可根据需求调整内存使用
中断控制 提供标准 API 控制中断,有两种类型的中断服务例程 传统上中断是操作系统专属领域,部分实时扩展项目提供中断控制接口

从上述对比可以看出,ERIKA 更适合对代码占用空间和实时性要求极高、资源有限的小型嵌入式系统,如一些简单的汽车电子设备、家用电器等。而嵌入式 Linux 则更适合功能需求复杂、需要丰富软件生态和网络功能的嵌入式系统,如互联网路由器、智能电视等。

7. 实际应用案例分析

为了更好地理解 ERIKA 和嵌入式 Linux 的应用场景,下面通过两个实际案例进行分析。

7.1 ERIKA 在汽车电子系统中的应用

在汽车电子系统中,许多功能模块对实时性和资源占用有严格要求。例如,发动机控制系统需要实时处理传感器数据并精确控制喷油、点火等操作,同时系统的内存和计算资源有限。

使用 ERIKA 实时内核可以很好地满足这些需求。通过 OSEK/VDX 标准的支持,开发人员可以使用 OIL 配置语言静态定义内核对象,避免动态内存分配带来的不确定性。同时,支持堆栈共享的特性可以显著减少 RAM 内存的使用,使得系统能够在有限的资源下稳定运行。

在任务管理方面,固定优先级调度和立即优先级上限机制可以保证关键任务的实时执行,避免优先级反转问题。例如,发动机控制任务可以设置为高优先级,确保在任何情况下都能及时响应。

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    SensorData[传感器数据] --> ERIKA[ERIKA 实时内核]:::process
    ERIKA --> EngineControl[发动机控制]:::process
    EngineControl --> Actuator[执行器]:::process
7.2 嵌入式 Linux 在智能路由器中的应用

智能路由器需要支持多种网络协议、提供丰富的网络服务,同时要具备一定的扩展性以适应不同的用户需求。嵌入式 Linux 凭借其开源、易移植和丰富的软件生态等特点,成为智能路由器的理想选择。

在系统结构方面,嵌入式 Linux 可以根据路由器的硬件平台和功能需求定制内核和系统组件。例如,选择合适的 C 库和实用工具,以减少代码占用空间和内存使用。BusyBox 可以提供基本的 Unix 实用程序,满足路由器的日常管理需求。

在网络功能方面,Linux 内核自带了丰富的网络协议栈,能够轻松支持以太网、Wi-Fi、VPN 等多种网络连接方式。同时,通过 RTAI 或 Xenomai 等实时扩展项目,可以为一些对实时性要求较高的网络应用提供支持,如视频会议、实时监控等。

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    Internet[互联网] --> Router[嵌入式 Linux 路由器]:::process
    Router --> Wi-Fi[Wi-Fi 设备]:::process
    Router --> Ethernet[以太网设备]:::process
    Router --> VPN[VPN 连接]:::process
8. 未来发展趋势

随着嵌入式系统应用领域的不断扩大和技术的不断进步,ERIKA 和嵌入式 Linux 也将面临新的挑战和机遇。

对于 ERIKA 来说,未来可能会进一步优化实时调度算法,提高系统的实时性能和可调度性。同时,随着物联网的发展,对低功耗、小体积嵌入式设备的需求增加,ERIKA 可能会在这些领域发挥更大的作用。

嵌入式 Linux 则有望在人工智能、边缘计算等领域得到更广泛的应用。通过与深度学习框架的集成,嵌入式 Linux 系统可以实现智能图像识别、语音交互等功能。此外,随着多核处理器的普及,如何更好地利用多核资源提高系统性能也是嵌入式 Linux 需要解决的问题。

9. 总结

本文详细介绍了 ERIKA 实时内核和嵌入式 Linux 的特点、应用场景以及发展趋势。ERIKA 以其小巧、实时性强的特点适用于资源受限的小型嵌入式系统,而嵌入式 Linux 则凭借其丰富的功能和强大的扩展性在复杂嵌入式系统中占据优势。

在实际应用中,需要根据具体的需求和场景选择合适的操作系统。无论是 ERIKA 还是嵌入式 Linux,都在不断发展和完善,为嵌入式系统的发展提供了有力的支持。通过深入了解它们的特性和应用,开发人员可以更好地设计和实现高效、稳定的嵌入式系统。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐