# Cobalt/Process scheduling

### 详细描述

Cobalt/POSIX 进程调度。

title: Cobalt/Process scheduling

### 函数文档

### sched_get_priority_max()

> int sched_get_priority_max (int policy)

获取指定调度策略的最大优先级。

该服务返回调度策略 policy 的最大优先级。

参数:

- policy: 调度策略。

返回值:

- 成功时返回 0。
- 出错时返回 -1 并设置 errno：
  - EINVAL: policy 无效。

标签:

- `thread-unrestricted`，`switch-secondary`

注意: 获取 `SCHED_FIFO`、`SCHED_RR` 或任何 xkernel 特定策略的最大优先级级别不会导致模式切换。policy 的任何其他值可能会将调用者切换到次级模式。

示例代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <sched.h>
    #include <errno.h>

    int main() {
        int policy = SCHED_FIFO; // 可以选择 SCHED_OTHER, SCHED_RR, SCHED_FIFO
        int max_priority;

        // 获取指定调度策略的最大优先级
        max_priority = sched_get_priority_max(policy);

        // 检查返回值
        if (max_priority == -1) {
            perror("sched_get_priority_max");
            exit(EXIT_FAILURE);
        }

        printf("The maximum priority for policy %d is: %d\n", policy, max_priority);

        return 0;
    }

#### sched_get_priority_max_ex()

> int sched_get_priority_max_ex (int policy)

获取指定调度策略的扩展最大优先级。

该服务返回调度策略 policy 的最大优先级，反映任何 Cobalt 对标准类的扩展。

参数:

- policy: 调度策略。

返回值:

- 成功时返回 0。
- 出错时返回 -1 并设置 errno：
  - EINVAL: policy 无效。

标签:

- `thread-unrestricted`

示例代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <sched.h>
    #include <errno.h>

    int main() {
        int policy = SCHED_FIFO; // 可以选择 SCHED_OTHER, SCHED_RR, SCHED_FIFO
        int max_priority;

        // 获取指定调度策略的最大优先级
        max_priority = sched_get_priority_max_ex(policy);

        // 检查返回值
        if (max_priority == -1) {
            perror("sched_get_priority_max_ex");
            exit(EXIT_FAILURE);
        }

        printf("The maximum priority for policy %d is: %d\n", policy, max_priority);

        return 0;
    }

#### sched_get_priority_min()

> int sched_get_priority_min (int policy)

获取指定调度策略的最小优先级。

该服务返回调度策略 policy 的最小优先级。

参数:

- policy: 调度策略。

返回值:

- 成功时返回 0。
- 出错时返回 -1 并设置 errno：
  - EINVAL: policy 无效。

标签:

- `thread-unrestricted`，`switch-secondary`

注意: 获取 SCHED_FIFO、SCHED_RR 或任何 xkernel 特定策略的最小优先级级别不会导致模式切换。policy 的任何其他值可能会将调用者切换到次级模式。

示例代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <sched.h>
    #include <errno.h>

    int main() {
        int policy = SCHED_FIFO; // 可以选择 SCHED_OTHER, SCHED_RR, SCHED_FIFO
        int min_priority;

        // 获取指定调度策略的最大优先级
        min_priority = sched_get_priority_min(policy);

        // 检查返回值
        if (min_priority == -1) {
            perror("sched_get_priority_min");
            exit(EXIT_FAILURE);
        }

        printf("The minimum priority for policy %d is: %d\n", policy, min_priority);

        return 0;
    }

### sched_get_priority_min_ex()

> int sched_get_priority_min_ex (int policy)

获取指定调度策略的扩展最小优先级。

该服务返回调度策略 policy 的最小优先级，反映任何 Cobalt 对标准类的扩展。

参数:

- policy: 调度策略。

返回值:

- 成功时返回 0。
- 出错时返回 -1 并设置 errno：
  - EINVAL: policy 无效。

标签:

- `thread-unrestricted`

示例代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <sched.h>
    #include <errno.h>

    int main() {
        int policy = SCHED_FIFO; // 可以选择 SCHED_OTHER, SCHED_RR, SCHED_FIFO
        int min_priority;

        // 获取指定调度策略的最大优先级
        min_priority = sched_get_priority_min_ex(policy);

        // 检查返回值
        if (min_priority == -1) {
            perror("sched_get_priority_min_ex");
            exit(EXIT_FAILURE);
        }

        printf("The minimum priority for policy %d is: %d\n", policy, min_priority);

        return 0;
    }

### sched_getconfig_np()

> ssize_t sched_getconfig_np (int cpu, int policy, union sched_config *config, size_t *len_r)

检索特定 CPU 的调度策略设置。

配置严格地局限于目标 cpu，并且可能与其他处理器不同。

参数:

- cpu: 要检索配置的处理器。
- policy: 配置数据适用的调度策略。目前，仅 `SCHED_TP` 和 `SCHED_QUOTA` 是有效输入。
- config: 一个指向内存区域的指针，该区域在调用成功时接收配置设置。

SCHED_TP 具体细节: 成功返回时，config->quota.tp 包含 cpu 上活动的 TP 调度。

SCHED_QUOTA 具体细节: 进入时，config->quota.get.tgid 必须包含要查询的线程组标识符。 成功退出时，config->quota.info 包含与 config->quota.get.tgid 引用的线程组相关的信息。

参数:

- [in, out] len_r: 一个指向变量的指针，用于收集返回的配置数据的总长度（以字节为单位）。在请求发出时，该变量必须包含 config 中可用的空间量。

返回值:

- 成功时返回复制到 config 的字节数；
- 出错时返回负错误号：
  - EINVAL: cpu 无效，或 policy 不受当前内核配置支持，或 len 无法容纳检索到的配置数据。
  - ESRCH，当 policy 等于 `SCHED_QUOTA` 时，如果执行操作所需的组标识符无效（即 config->quota.get.tgid 无效）。
  - ENOMEM: 执行操作时内存不足。
  - ENOSPC: len 太短。

标签:

- `thread-unrestricted`，`switch-primary`

示例代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <sched.h>
    #include <pthread.h>
    #include <string.h>
    #include <unistd.h>
    #include <cobalt/sched.h>

    void *thread_function(void *arg) {
        int cpu = 0;  // 目标 CPU
        union sched_config config;
        size_t len;

        // 设置 TP 调度窗口
        config.tp.op = sched_tp_install;
        config.tp.nr_windows = 1;

        // 配置1个时间窗口
        config.tp.windows[0].offset.tv_sec = 0;
        config.tp.windows[0].offset.tv_nsec = 10000000;
        config.tp.windows[0].duration.tv_sec = 0;
        config.tp.windows[0].duration.tv_nsec = 10000000;  // 10ms
        config.tp.windows[0].ptid = 2;

        len = sizeof(config.tp);

        // 设置 SCHED_TP 配置
        if (sched_setconfig_np(cpu, SCHED_TP, &config, len) < 0) {
            perror("sched_setconfig_np");
            exit(EXIT_FAILURE);
        }

         // 检索 SCHED_TP 配置
        len = sizeof(config);
        if (sched_getconfig_np(cpu, SCHED_TP, &config, &len) < 0) {
            perror("sched_getconfig_np");
            exit(EXIT_FAILURE);
        }

        printf("Successfully retrieved SCHED_TP configuration for CPU %d\n", cpu);
        printf("Number of windows: %d\n", config.tp.nr_windows);

        // 打印每个时间窗口的信息
        for (int i = 0; i < config.tp.nr_windows; i++) {
            printf("Window %d:\n", i);
            printf("  Offset: %ld.%09ld\n",
                   config.tp.windows[i].offset.tv_sec,
                   config.tp.windows[i].offset.tv_nsec);
            printf("  Duration: %ld.%09ld\n",
                   config.tp.windows[i].duration.tv_sec,
                   config.tp.windows[i].duration.tv_nsec);
            printf("  Partition ID: %d\n", config.tp.windows[i].ptid);
        }

        // 启动 TP 调度
        config.tp.op = sched_tp_start;
        if (sched_setconfig_np(cpu, SCHED_TP, &config, len) < 0) {
            perror("sched_setconfig_np (start)");
            exit(EXIT_FAILURE);
        }

        printf("Thread running with PID: %d\n", getpid());
        for (int i = 0; i < 3; i++) {
            printf("Thread: %d\n", i);
            sleep(1);
        }

        //停止TP调度并卸载
        config.tp.op = sched_tp_stop;
        if (sched_setconfig_np(cpu, SCHED_TP, &config, len) < 0) {
            perror("sched_setconfig_np (start)");
            exit(EXIT_FAILURE);
        }
        config.tp.op = sched_tp_uninstall;
        if (sched_setconfig_np(cpu, SCHED_TP, &config, len) < 0) {
            perror("sched_setconfig_np (start)");
            exit(EXIT_FAILURE);
        }
        pthread_exit(0);
    }

    int main() {
        pthread_t thread;

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

        // 等待线程结束
        pthread_join(thread, NULL);
        printf("Thread finished.\n");

        return EXIT_SUCCESS;
    }

### sched_setconfig_np()

> int sched_setconfig_np (int cpu, int policy, const union sched_config *config, size_t len)

为特定 CPU 设置调度策略配置。

配置严格地局限于目标 cpu，并且可能与其他处理器不同。

参数:

- cpu: 要加载配置的处理器。
- policy: 配置数据适用的调度策略。目前，SCHED_TP 和 SCHED_QUOTA 是有效的。
- config: 一个指向要加载到 cpu 上的配置数据的指针，适用于 policy。

#### 适用于 SCHED_TP 的设置

此调用根据请求的操作控制 cpu 的时间分区。

- config.tp.op 指定要执行的操作：
  - sched_tp_install 在 cpu 上安装一个新的 TP 调度，由 config.tp.windows[] 定义。全局时间框架在此请求返回时尚未激活；必须发出 sched_tp_start 才能在 cpu 上激活时间调度。
  - sched_tp_uninstall 从 cpu 上移除当前的 TP 调度，释放所有附加资源。如果 cpu 上不存在 TP 调度，则此请求无效。
  - sched_tp_start 启用 cpu 上的时间调度，启动全局时间框架。如果 cpu 上不存在 TP 调度，则此操作无效。
  - sched_tp_stop 禁用 cpu 上的时间调度。不过，当前的 TP 调度不会被卸载，可以通过 sched_tp_start 请求稍后重新启动。

注意:

- 由于此请求的结果，分配给未调度分区的线程可能会被剥夺 CPU 时间。

#### 适用于 SCHED_QUOTA 的设置

此调用管理在 cpu 上运行的线程组，定义每组的配额以限制其 CPU 消耗。

- config.quota.op 应定义要执行的操作。有效操作包括：
  - sched_quota_add 在 cpu 上创建一个新的线程组。新组标识符将在成功时写回到 info.tgid。
  - sched_quota_remove 删除 cpu 上的线程组。组标识符应传递到 config.quota.remove.tgid。
  - sched_quota_set 更新在 cpu 上定义的线程组的调度参数。

参数:

- len: 配置数据的总长度（以字节为单位）。

返回值:

- 成功时返回 0；
- 出错时返回错误号：
  - EINVAL: cpu 无效，或 policy 不受当前内核配置支持，或 len 无效，或 config 包含无效参数。
  - ENOMEM: 执行操作时内存不足。
  - EBUSY: 当 policy 等于 SCHED_QUOTA 时，如果尝试删除仍在管理线程的线程组。
  - ESRCH: 当 policy 等于 SCHED_QUOTA 时，如果执行操作所需的组标识符无效。

标签:

- `thread-unrestricted`，`switch-primary`

示例代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <sched.h>
    #include <pthread.h>
    #include <string.h>
    #include <unistd.h>
    #include <cobalt/sched.h>

    void *thread_function(void *arg) {
        int cpu = 0;  // 目标 CPU
        union sched_config config;
        size_t len;

        // 设置 TP 调度窗口
        config.tp.op = sched_tp_install;
        config.tp.nr_windows = 1;

        // 配置1个时间窗口
        config.tp.windows[0].offset.tv_sec = 0;
        config.tp.windows[0].offset.tv_nsec = 0;
        config.tp.windows[0].duration.tv_sec = 0;
        config.tp.windows[0].duration.tv_nsec = 10000000;  // 10ms
        config.tp.windows[0].ptid = 1;

        len = sizeof(config.tp);

        // 设置 SCHED_TP 配置
        if (sched_setconfig_np(cpu, SCHED_TP, &config, len) < 0) {
            perror("sched_setconfig_np");
            exit(EXIT_FAILURE);
        }

        printf("Successfully set SCHED_TP configuration for CPU %d\n", cpu);

        // 启动 TP 调度
        config.tp.op = sched_tp_start;
        if (sched_setconfig_np(cpu, SCHED_TP, &config, len) < 0) {
            perror("sched_setconfig_np (start)");
            exit(EXIT_FAILURE);
        }

        printf("Started SCHED_TP scheduling on CPU %d\n", cpu);

        printf("Thread running with PID: %d\n", getpid());
        for (int i = 0; i < 3; i++) {
            printf("Thread: %d\n", i);
            sleep(1);
        }
        // 停止TP调度并卸载
        config.tp.op = sched_tp_stop;
        if (sched_setconfig_np(cpu, SCHED_TP, &config, len) < 0) {
            perror("sched_setconfig_np (start)");
            exit(EXIT_FAILURE);
        }
        config.tp.op = sched_tp_uninstall;
        if (sched_setconfig_np(cpu, SCHED_TP, &config, len) < 0) {
            perror("sched_setconfig_np (start)");
            exit(EXIT_FAILURE);
        }
        pthread_exit(0);
    }

    int main() {
        pthread_t thread;

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

        // 等待线程结束
        pthread_join(thread, NULL);
        printf("Thread finished.\n");

        return EXIT_SUCCESS;
    }

### sched_getscheduler()

> int sched_getscheduler (pid_t pid)

获取指定进程的调度策略。

该服务检索由 pid 标识的 Cobalt 进程的调度策略。

如果 pid 未标识现有的 Cobalt 线程/进程，则该服务回退到常规的 `sched_getscheduler()` 服务。

参数:

- pid: 目标进程/线程。

返回值:

- 成功时返回 0；
- 出错时返回错误号：
  - ESRCH: 找不到 pid；
  - EINVAL: pid 为负数；
  - EFAULT: param_ex 是无效地址。

标签:

- `thread-unrestricted`

示例代码

    #include <stdio.h>
    #include <sched.h>
    #include <stdlib.h>
    #include <errno.h>

    int main() {
        // 获取当前进程的调度策略
        int policy = sched_getscheduler(0);
        if (policy == -1) {
            perror("sched_getscheduler failed");
            exit(EXIT_FAILURE);
        }

        // 根据获取的策略打印对应的调度策略名称
        switch (policy) {
            case SCHED_OTHER:
                printf("Current policy: SCHED_OTHER\n");
                break;
            case SCHED_FIFO:
                printf("Current policy: SCHED_FIFO\n");
                break;
            case SCHED_RR:
                printf("Current policy: SCHED_RR\n");
                break;
            default:
                printf("Current policy: Unknown (%d)\n", policy);
                break;
        }

        return 0;
    }

### sched_getscheduler_ex()

> int sched_getscheduler_ex (pid_t pid, int *policy_r, struct sched_param_ex *param_ex)

获取进程的扩展调度策略。

该服务是 `sched_getscheduler()` 服务的扩展版本，支持 Cobalt 特定和/或主机 Linux 环境不可用的附加调度策略。它检索由 pid 标识的 Cobalt 进程/线程的调度策略及相关的调度参数（例如优先级）。

参数:

- pid: 查询的进程/线程。如果为零，则假定为当前线程。
- policy_r: 一个指向变量的指针，用于接收 pid 的当前调度策略。
- param_ex: 一个指向结构的指针，用于接收 pid 的当前调度参数。

返回值:

- 成功时返回 0；
- 出错时返回错误号：
  - ESRCH: pid 不是 Cobalt 线程；
  - EINVAL: pid 为负数或 param_ex 为 NULL；
  - EFAULT: param_ex 是无效地址。

标签:

- `thread-unrestricted`

示例代码

    #include <stdio.h>
    #include <sched.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <cobalt/sched.h>

    int main() {
        struct sched_param_ex param;
        int policy;

        // 获取当前进程的调度策略和参数
        sched_getscheduler_ex(0,&policy,&param);
        if (policy == -1) {
            perror("sched_getscheduler failed");
            exit(EXIT_FAILURE);
        }

        // 根据获取的策略打印对应的调度策略名称
        switch (policy) {
            case SCHED_OTHER:
                printf("Current policy: SCHED_OTHER\n");
                break;
            case SCHED_FIFO:
                printf("Current policy: SCHED_FIFO\n");
                break;
            case SCHED_RR:
                printf("Current policy: SCHED_RR\n");
                break;
            default:
                printf("Current policy: Unknown (%d)\n", policy);
                break;
        }
        // 打印当前进程的调度优先级
        printf("Current priority: %d\n", param.sched_priority);

        return 0;
    }

#### sched_setscheduler()

> int sched_setscheduler (pid_t pid, int policy, const struct sched_param *param)

设置指定进程的调度策略和参数。

该服务将由 pid 标识的 Cobalt 进程的调度策略设置为 policy，并将其调度参数（即优先级）设置为 param 指向的值。

如果传递当前的 Linux 线程 ID（参见 `gettid(2)`），该服务会将当前的常规 POSIX 线程转换为 Cobalt 线程。如果 pid 既不是当前线程的标识符，也不是现有 Cobalt 线程的标识符，则该服务回退到常规的 `sched_setscheduler()` 服务。

参数:

- pid: 目标进程/线程；
- policy: 调度策略，`SCHED_FIFO`、`SCHED_RR` 或 `SCHED_OTHER` 之一；
- param: 调度参数地址。

返回值:

- 成功时返回 0；
- 出错时返回错误号：
  - ESRCH: pid 无效；
  - EINVAL: policy 或 param->sched_priority 无效；
  - EAGAIN: 系统堆内存不足，请增加 `CONFIG_XENO_OPT_SYS_HEAPSZ`；
  - EFAULT: param 是无效地址。

注意:

- 参见 `sched_setscheduler_ex()`。

标签:

- `thread-unrestricted`，`switch-secondary`，`switch-primary`

示例代码

    #include <stdio.h>
    #include <sched.h>
    #include <stdlib.h>
    #include <errno.h>

    int main() {
        // 获取当前进程的调度策略
        int policy = sched_getscheduler(0);
        if (policy == -1) {
            perror("sched_getscheduler failed");
            exit(EXIT_FAILURE);
        }
         // 获取当前进程的调度参数
        struct sched_param param;
        if (sched_getparam(0, &param) == -1) {
            perror("sched_getparam failed");
            exit(EXIT_FAILURE);
        }
        printf("Befor-Set : policy[%d] priority[%d]\n",policy, param.sched_priority);

        param.sched_priority = 15;
        if (sched_setscheduler(0, SCHED_FIFO, &param) == -1) {
            perror("sched_getparam failed");
            exit(EXIT_FAILURE);
        }
        policy = sched_getscheduler(0);
        if (policy == -1) {
            perror("sched_getscheduler failed");
            exit(EXIT_FAILURE);
        }
         // 获取当前进程的调度参数
        if (sched_getparam(0, &param) == -1) {
            perror("sched_getparam failed");
            exit(EXIT_FAILURE);
        }
        printf("After-Set : policy[%d] priority[%d]\n",policy, param.sched_priority);

        return 0;
    }

#### sched_setscheduler_ex()

> int sched_setscheduler_ex (pid_t pid, int policy, const struct sched_param_ex *param_ex)

设置进程的扩展调度策略。

该服务是 `sched_setscheduler()` 服务的扩展版本，支持 Cobalt 特定和/或主机 Linux 环境不可用的附加调度策略。它将由 pid 标识的 Cobalt 进程/线程的调度策略设置为 policy 的值，并将其调度参数（例如优先级）设置为 param_ex 指向的值。

如果传递当前的 Linux 线程 ID 或零（参见 `gettid(2)`），该服务可能会将当前的常规 POSIX 线程转换为 Cobalt 线程。

参数:

- pid: 目标进程/线程。如果为零，则假定为当前线程。
- policy: 调度策略，`SCHED_WEAK`、`SCHED_FIFO`、`SCHED_COBALT`、`SCHED_RR`、`SCHED_SPORADIC`、`SCHED_TP`、`SCHED_QUOTA` 或 `SCHED_NORMAL` 之一。
- param_ex: 调度参数的地址。作为一个特殊例外，负的 sched_priority 值被解释为 policy 中给定的 SCHED_WEAK，使用该参数的绝对值作为弱优先级级别。

当启用 `CONFIG_XENO_OPT_SCHED_WEAK` 时，`SCHED_WEAK` 在 [0..99] 范围内（包括 0 和 99）展示优先级级别。否则，对于 `SCHED_WEAK` 策略，sched_priority 必须为零。

返回值:

- 成功时返回 0；
- 出错时返回错误号：
  - ESRCH: 找不到 pid；
  - EINVAL: pid 为负数，param_ex 为 NULL，policy 或 param_ex->sched_priority 无效；
  - EAGAIN: 系统堆内存不足，请增加 `CONFIG_XENO_OPT_SYS_HEAPSZ`；
  - EFAULT: param_ex 是无效地址；

注意:

- 参见 `sched_setscheduler()`。

标签:

- `thread-unrestricted`，`switch-secondary`，`switch-primary`

示例代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <cobalt/sched.h>

    int main() {
        struct sched_param param;
        int policy;
        // 获取当前进程的调度策略和参数
        sched_getscheduler_ex(0,&policy,&param);
        if (policy == -1) {
            perror("sched_getscheduler failed");
            exit(EXIT_FAILURE);
        }
        printf("Befor-Set : policy[%d] priority[%d]\n",policy, param.sched_priority);

        param.sched_priority = 15;
        if (sched_setscheduler(0, SCHED_FIFO, &param) == -1) {
            perror("sched_getparam failed");
            exit(EXIT_FAILURE);
        }

        sched_getscheduler_ex(0,&policy,&param);
        if (policy == -1) {
            perror("sched_getscheduler failed");
            exit(EXIT_FAILURE);
        }
        printf("After-Set : policy[%d] priority[%d]\n",policy, param.sched_priority);

        return 0;
    }

#### sched_yield()

> int sched_yield (void)

让出处理器。

此函数将当前线程移动到其优先级组的末尾。

返回值:

- 0

标签:

- `thread-unrestricted`，`switch-primary`

引用 `XNRELAX` 和 `XNWEAK`。

被 `pthread_yield()` 引用。

示例代码

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

    #define NUM_THREADS 3

    void* thread_function(void* arg) {
        int thread_id = *((int*)arg);
        for (int i = 0; i < 5; ++i) {
            printf("Thread %d: working...\n", thread_id);
            // 模拟工作
            usleep(100000); // 睡眠 100 毫秒

            // 调用 sched_yield() 放弃 CPU
            printf("Thread %d: yielding...\n", thread_id);
            sched_yield();
        }
        return NULL;
    }

    int main() {
        pthread_t threads[NUM_THREADS];
        int thread_ids[NUM_THREADS];

        // 创建多个线程
        for (int i = 0; i < NUM_THREADS; ++i) {
            thread_ids[i] = i + 1;
            if (pthread_create(&threads[i], NULL, thread_function, &thread_ids[i]) != 0) {
                perror("Failed to create thread");
                exit(EXIT_FAILURE);
            }
        }

        // 等待所有线程完成
        for (int i = 0; i < NUM_THREADS; ++i) {
            pthread_join(threads[i], NULL);
        }

        printf("All threads have finished.\n");
        return 0;
    }