28、嵌入式操作系统:系统资源分离与多任务调度

嵌入式操作系统:系统资源分离与多任务调度

1. 系统资源分离

为了限制当前任务直接访问资源,采取了进一步的限制措施。示例系统通过以下代码设置了内存保护单元(MPU):

DMB();
mpu_set_region(3, (uint32_t)start, attr);
MPU_CTRL = 1;

这些限制使得任务无法直接控制输入输出外设等系统资源,甚至 sleep_ms 函数也不能设置挂起标志来发起上下文切换。为了维持原有功能,系统需要导出新的安全 API 供任务请求系统操作。

2. 系统调用

操作系统通过 SVCall 异常的系统调用机制导出 API,该异常由 isr_svc 服务例程处理,任务可通过 svc 指令随时触发。示例中使用 svc 0 汇编指令切换到处理模式,并定义了快捷宏 SVC()

#define SVC() asm volatile ("svc 0")

将该指令封装在 C 函数中以传递参数:

static int syscall(int arg0)
{
    SVC();
}

系统调用列表如下:
| 系统调用 | 编号 | 说明 |
| ---- | ---- | ---- |
| SYS_SCHEDULE | 0 | 调度下一个任务 |
| SYS_BUTTON_READ | 1 | 读取按钮值 |
| SYS_BLUELED_ON | 2 | 打开蓝色 LED |
| SYS_BLUELED_OFF | 3 | 关闭蓝色 LED |
| SYS_BLUELED_TOGGLE | 4 | 切换蓝色 LED 状态 |
| SYS_REDLED_ON | 5 | 打开红色 LED |
| SYS_REDLED_OFF | 6 | 关闭红色 LED |
| SYS_REDLED_TOGGLE | 7 | 切换红色 LED 状态 |
| SYS_GREENLED_ON | 8 | 打开绿色 LED |
| SYS_GREENLED_OFF | 9 | 关闭绿色 LED |
| SYS_GREENLED_TOGGLE | 10 | 切换绿色 LED 状态 |

isr_svc 函数用于处理这些系统调用:

void __attribute__((naked)) isr_svc(int arg)
{
    store_user_context();
    asm volatile("mrs %0, psp" : "=r"(t_cur->sp));
    if (t_cur->state == TASK_RUNNING) {
        t_cur->state = TASK_READY;
    }
    switch(arg) {
        case SYS_BUTTON_READ: /* cmd to read button value */
            button_start_read();
            break;
        case SYS_SCHEDULE: /* cmd to schedule the next task */
            t_cur = tasklist_next_ready(t_cur);
            t_cur->state = TASK_RUNNING;
            break;
        case SYS_BLUELED_ON: /* cmd to turn on blue LED */
            blue_led_on();
            break;
        /* case ... (more LED related cmds follow) */
    }
    if (t_cur->id == 0) {
        asm volatile("msr msp, %0" ::"r"(t_cur->sp));
        restore_kernel_context();
        asm volatile("mov lr, %0" ::"r"(0xFFFFFFF9));
        asm volatile("msr CONTROL, %0" ::"r"(0x00));
    } else {
        asm volatile("msr psp, %0" ::"r"(t_cur->sp));
        restore_user_context();
        mpu_task_stack_permit(((uint8_t *)((&stack_space))
                              +(t_cur->id << 10)));
        asm volatile("mov lr, %0" ::"r"(0xFFFFFFFD));
        asm volatile("msr CONTROL, %0" ::"r"(0x01));
    }
    asm volatile("bx lr");
}

以下是使用系统调用的示例任务代码:

void task_test0(void *arg)
{
    while(1) {
        syscall(SYS_BLUELED_ON);
        mutex_lock(&m);
        sleep_ms(500);
        syscall(SYS_BLUELED_OFF);
        mutex_unlock(&m);
        sleep_ms(1000);
    }
}

void task_test1(void *arg)
{
    syscall(SYS_REDLED_ON);
    while(1) {
        sleep_ms(50);
        mutex_lock(&m);
        syscall(SYS_REDLED_TOGGLE);
        mutex_unlock(&m);
    }
}

void task_test2(void *arg)
{
    uint32_t toggle_time = 0;
    syscall(SYS_GREENLED_OFF);
    while(1) {
        button_read();
        if ((jiffies - toggle_time) > 120) {
            syscall(SYS_GREENLED_TOGGLE);
            toggle_time = jiffies;
        }
    }
}
3. 嵌入式操作系统选择

选择适合目标平台和用途的嵌入式操作系统是一项复杂的任务,需要考虑硬件特性、与第三方库的集成、外设交互能力以及系统的使用场景等因素。常见的嵌入式操作系统功能套件包括:
- 特定平台的硬件抽象层
- 常见外设的设备驱动
- 用于连接性的 TCP/IP 栈集成
- 文件系统和文件抽象
- 集成电源管理系统

根据调度器中线程模型的实现,有些系统在编译时配置固定数量的任务,而有些则支持在运行时创建和终止线程。但在嵌入式系统中,动态任务创建和终止的需求较少。

4. 流行的开源嵌入式操作系统
4.1 FreeRTOS

FreeRTOS 是嵌入式设备中最流行的开源操作系统之一,具有高度的可移植性,支持多种嵌入式 CPU 架构。它专注于线程的实时调度和堆内存管理,代码简洁,接口简单。

  • 调度器 :采用抢占式调度,具有固定优先级和通过共享互斥锁的优先级继承机制。线程的优先级和栈空间大小在创建时确定。
xTaskCreate(task_entry_fn, "TaskName", task_stack_size,
            ( void * ) custom_params, priority, task_handle);

创建任务后,调用 vTaskStartScheduler() 启动调度器。

  • 堆内存管理 :提供五种堆内存管理模型:
    • Heap 1:仅允许一次性静态分配,无法释放内存。
    • Heap 2:允许释放内存,但不重新组合已释放的块。
    • Heap 3:封装第三方库的 malloc/free 实现,确保线程安全。
    • Heap 4:支持内存合并,减少堆碎片化。
    • Heap 5:与 Heap 4 类似,但允许定义多个非连续的内存区域作为同一堆空间。

选择特定的堆模型只需包含相应的源文件,如 heap_1.c heap_2.c 等。重要的堆内存管理函数是 pvPortMalloc pvPortFree

  • 其他特性 :支持 MPU 和线程模式,线程可在受限模式下运行;低功耗管理限于睡眠模式,但允许重新定义调度器回调函数进入自定义低功耗模式;近期版本包含第三方代码,用于构建物联网安全连接平台。
graph LR
    A[创建任务] --> B[启动调度器]
    B --> C[任务调度]
    C --> D{任务状态}
    D -->|运行| E[执行任务]
    D -->|就绪| F[等待调度]
    D -->|阻塞| G[等待资源]

嵌入式操作系统:系统资源分离与多任务调度

5. RIOT OS

RIOT OS 主要构建在受限微控制器上,适用于低功耗嵌入式系统,如物联网项目和长期运行无需维护的场景。它旨在以最少的资源组织和同步任务,同时注重节能和连接性。

5.1 系统特点
  • 丰富的库和设备支持 :与 FreeRTOS 的极简主义不同,RIOT OS 提供了广泛的库和设备支持代码,包括网络栈和无线驱动通信,对物联网应用友好。非核心功能的组件分为可选模块,通过自定义的基于 makefile 的构建系统方便集成到应用中。
  • 类 POSIX 接口 :从 API 角度,RIOT 社区尽量模仿 POSIX 接口,方便不同背景的程序员使用标准 C 语言 API 访问系统资源。不过,系统采用扁平模型,未在系统层面实现特权分离,用户空间应用仍需直接引用系统内存来访问资源。
  • MPU 安全措施 :为提高安全性,可使用 MPU 检测单线程的栈溢出。在栈底部设置一个小的只读区域,若线程试图写入超出分配的栈空间,将触发异常。
5.2 网络通信

RIOT OS 实现了多个通信栈作为模块,其中包括专为 802.15.4 网络定制的 IPv6 极简 IP 栈 GNRC,并提供了套接字实现,用于编写轻量级物联网应用。此外,还包含 lwIP 兼容性层,可在需要时提供更完整的 TCP/IP 实现。同时,WolfSSL 模块可用于实现安全套接字通信和数据加密。

5.3 低功耗管理

通过电源管理模块,RIOT OS 集成了低功耗模式的配置功能。该模块提供了管理特定平台功能的抽象层,如 Cortex - M 平台的停止和待机模式。应用程序可在运行时激活低功耗模式,并利用实时时钟、看门狗定时器或其他外部信号返回正常运行模式。

5.4 调度器

RIOT OS 的调度器是无滴答且基于协作的。任务可通过调用 task_yield 函数或任何阻塞函数(如访问内核功能或硬件外设)来显式挂起自己。系统不会基于时间片强制并发,只有在接收到硬件中断时才会强制中断任务。因此,使用该调度器编程时需特别注意,避免在一个任务中意外创建忙循环,导致整个系统饥饿。

任务可通过 thread_create 函数创建:

thread_create(task_stack_mem, task_stack_size, priority, 
              flags, task_entry_fn, (void*)custom_args, "TaskName");

与 FreeRTOS 不同,调用者必须为创建任务的栈空间分配内存,这增加了调用者的代码量,但也提供了更多自定义栈空间位置的灵活性。由于调度器无滴答,无需手动启动,任务可在执行过程中的任何时间创建和停止。

5.5 堆内存管理

考虑到嵌入式目标设备的 RAM 有限,RIOT OS 不鼓励使用动态分配的内存,但提供了三种堆内存管理方法:
| 管理方法 | 特点 |
| ---- | ---- |
| 一次性静态分配 | 类似于 FreeRTOS 的“Heap 1”模型,分配的内存无法释放或重用。默认情况下, malloc 函数使用此实现, free 函数无效。 |
| 内存数组分配器 | 使用静态分配的缓冲区作为内存池,用于固定大小的伪动态分配请求。适用于处理多个相同大小缓冲区的场景,有自定义 API,不修改默认 malloc 函数的行为。 |
| 两级分离适配(TLSF)分配器 | 基于为 RTOS 优化的算法,支持多个内存池,提供动态内存支持并处理实时截止日期。作为可选模块,编译时会替换一次性分配器提供的 malloc free 函数。 |

6. 总结

不同的嵌入式操作系统适用于不同的应用场景。FreeRTOS 以其简单的设计、高度的可移植性和丰富的堆内存管理选项,适合对实时调度和内存管理有严格要求的应用。而 RIOT OS 则凭借其对低功耗和物联网连接性的关注,以及丰富的库和设备支持,成为低功耗嵌入式系统和物联网项目的理想选择。在选择嵌入式操作系统时,开发者应根据具体的硬件平台、应用需求和性能要求进行综合考虑。

graph LR
    A[启动 RIOT OS] --> B[创建任务]
    B --> C[任务执行]
    C --> D{任务状态}
    D -->|运行| E[执行任务代码]
    D -->|挂起| F[等待唤醒]
    D -->|阻塞| G[等待资源或事件]
    E --> H{是否有硬件中断}
    H -->|是| I[中断任务]
    H -->|否| C
    I --> J[处理中断]
    J --> C
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值