# 实时优先级

在实时系统开发中，线程的调度策略和优先级设置对于确保系统的实时性和可靠性至关重要。`xkernel` 的 `Cobalt` 实时内核提供了多种调度策略，使得开发者可以根据应用需求精确地控制线程的执行。本篇文章将深入探讨实时调度策略 `SCHED_FIFO` 的使用方法、设置方式以及可能遇到的问题，帮助您在实时编程中有效地管理线程的优先级。

### 实时调度策略（SCHED_FIFO）

#### 什么是 SCHED_FIFO？

- `SCHED_FIFO`（先进先出）是一种实时调度策略，属于 `POSIX` 标准定义的实时调度策略之一。
- 使用 `SCHED_FIFO` 调度策略的线程被视为实时线程，具有较高的调度优先级。
- 在 `Cobalt` 调度器中，实时线程需要使用 `SCHED_FIFO` 或 `SCHED_RR`（时间片轮转）调度策略。

#### SCHED_FIFO 的特性

- 优先级调度：线程按照其设定的优先级进行调度，优先级高的线程总是优先运行。
- 非抢占式：一旦线程获得 CPU 控制权，只要没有更高优先级的线程可运行，它将一直运行下去，直到其主动放弃或被阻塞。
- 先进先出：在相同优先级的线程中，按照它们准备就绪的顺序调度，先到先得。

#### 使用 SCHED_FIFO 的意义

- 确保实时性：通过精确控制线程的优先级和调度顺序，保证关键任务在需要的时间窗口内执行。
- 降低延迟：减少线程切换和调度延迟，满足对高实时性要求的应用，如工业控制、机器人、航空航天等领域。

#### 设置线程调度策略

在 `xkernel` 的 `Cobalt` 环境中，设置线程的调度策略和优先级需要使用 `POSIX` 线程库提供的函数。以下是设置线程调度策略的详细步骤。

1. 定义线程属性对象

在创建线程之前，需要定义并初始化一个 `pthread_attr_t` 类型的线程属性对象：

    pthread_attr_t attr;
    pthread_attr_init(&attr);

2. 设置调度策略和优先级

设置调度策略

使用 `pthread_attr_setschedpolicy()` 函数设置线程的调度策略：

    pthread_attr_setschedpolicy(&attr, SCHED_FIFO);

设置线程继承性

默认情况下，线程会继承其父线程的调度属性。为了明确指定线程的调度属性，需要将继承性设置为 不继承：

    pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);

设置调度参数（优先级）

定义一个 `sched_param` 结构体，并设置线程的优先级。`Cobalt` 的优先级范围通常在 1 到 99 之间，数字越大优先级越高。

    struct sched_param param;
    param.sched_priority = 80; // 设置优先级为 80
    pthread_attr_setschedparam(&attr, &param);

3. 创建线程

使用设置好的属性创建线程：

    pthread_t thread;
    pthread_create(&thread, &attr, thread_function, NULL);

4. 销毁线程属性对象

创建线程后，可以销毁线程属性对象以释放资源：

    pthread_attr_destroy(&attr);

完整示例

    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>

    void* thread_function(void* arg) {
        // 线程执行的代码
        while (1) {
            // 实时任务处理
        }
        return NULL;
    }

    int main() {
        pthread_t thread;
        pthread_attr_t attr;
        struct sched_param param;
        int ret;

        // 初始化线程属性对象
        pthread_attr_init(&attr);

        // 设置调度策略为 SCHED_FIFO
        pthread_attr_setschedpolicy(&attr, SCHED_FIFO);

        // 设置继承性为 EXPLICIT
        pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);

        // 设置优先级
        param.sched_priority = 80;
        pthread_attr_setschedparam(&attr, &param);

        // 创建线程
        ret = pthread_create(&thread, &attr, thread_function, NULL);
        if (ret != 0) {
            perror("pthread_create failed");
            exit(EXIT_FAILURE);
        }

        // 销毁线程属性对象
        pthread_attr_destroy(&attr);

        // 主线程执行其他操作或等待子线程结束
        pthread_join(thread, NULL);

        return 0;
    }

#### 修改已存在线程的调度参数

如果需要在线程创建后修改其调度策略或优先级，可以使用 `pthread_setschedparam()` 函数。

    pthread_t thread = pthread_self(); // 获取当前线程
    struct sched_param param;
    param.sched_priority = 75;
    pthread_setschedparam(thread, SCHED_FIFO, &param);

#### 潜在的问题

- 无限循环风险

问题描述

使用 `SCHED_FIFO` 调度策略的线程如果包含无限循环，且没有适当的同步机制或主动放弃 CPU 的操作，可能导致系统被锁定。这是因为：

- 非抢占式调度：线程一旦获得 CPU 控制权，将一直运行，除非被更高优先级的线程抢占或自身阻塞。
- 资源独占：没有其他高优先级线程时，该线程会独占 CPU，导致其他线程无法获得执行机会，包括系统的关键任务和服务线程。

示例代码

以下代码可能导致系统锁定：

    pthread_mutex_lock(&mutex);
    while (!condition_met)
        pthread_cond_wait(&cond, &mutex);
    pthread_mutex_unlock(&mutex);

- 问题原因：
  - `condition_met` 条件变量或 `mutex` 互斥锁未正确初始化或已被销毁。
  - 导致 `pthread_cond_wait()` 无法正常阻塞线程，线程陷入无限循环，占用 CPU。

后果

- 系统无响应：其他线程无法获得执行机会，系统可能出现死锁或响应延迟。
- 实时性丧失：关键的实时任务无法按时执行，导致系统无法满足实时性要求。

避免方法

- 正确初始化同步对象：确保互斥锁、条件变量等在使用前已正确初始化。

    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

- 检查返回值：在使用同步函数时，检查返回值以确保操作成功。

    int ret = pthread_mutex_lock(&mutex);
    if (ret != 0) {
        // 处理错误
    }

- 添加超时机制：在等待条件时，可以设置超时时间，避免无限期等待。

    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    ts.tv_sec += 5; // 等待 5 秒

    int ret = pthread_cond_timedwait(&cond, &mutex, &ts);
    if (ret == ETIMEDOUT) {
        // 处理超时情况
    }

- 主动放弃 CPU：在适当的位置调用 `sched_yield()` 或 `pthread_yield()`，让出 CPU 控制权。

    while (!condition_met) {
        // 执行一些检查或处理
        sched_yield(); // 让出 CPU
    }

#### 优先级反转问题

问题描述

当高优先级线程等待被低优先级线程持有的资源时，可能发生优先级反转，导致实时性受损。

解决方案

- 优先级继承(Cobalt 默认使用这种方式)：使用互斥锁的优先级继承属性，确保低优先级线程在持有资源时提升到与高优先级线程相同的优先级。

    pthread_mutexattr_t mutex_attr;
    pthread_mutexattr_init(&mutex_attr);
    pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT);
    pthread_mutex_init(&mutex, &mutex_attr);

在使用 `Cobalt` 进行实时编程时，正确设置线程的调度策略和优先级对于系统的性能和稳定性至关重要。`SCHED_FIFO` 作为一种常用的实时调度策略，提供了对线程执行的精确控制，但同时也带来了潜在的风险。