Linux中的实时线程

一、Linux线程调度策略

  在 Linux 中,调度策略(scheduling policy)是操作系统用来决定进程或线程调度顺序的算法。在 <sched.h> 头文件中定义了几种调度策略,其中包括以下三种常见的调度策略:

#define SCHED_OTHER		0
#define SCHED_FIFO		1
#define SCHED_RR		2
  1. SCHED_OTHER:又称为 CFS(Completely Fair Scheduler),是 Linux 默认的调度策略。它基于时间片(time-slice)的概念,使用公平调度算法,以尽量保证所有进程或线程公平地分享 CPU 时间。对于大多数普通应用程序来说,默认的 SCHED_OTHER 调度策略已经足够。

  2. SCHED_FIFO:先进先出调度策略,也称为实时(real-time)调度策略之一。按照任务到达顺序依次运行,直到任务主动释放 CPU 或者被更高优先级的任务抢占。

  3. SCHED_RR:轮转调度策略,也是一种实时调度策略。每个任务都按照一定时间片轮流使用 CPU,当时间片用完后,任务会被挂起并被放置到队列的末尾,然后下一个任务开始执行。SCHED_RR 调度策略允许设置不同的任务优先级,优先级高的任务会在优先级低的任务之前执行。

  这些调度策略可以通过 sched_setscheduler 函数来设置进程或线程的调度策略。以下是一个示例,展示了如何使用 sched_setscheduler 函数将进程的调度策略设置为 SCHED_FIFO

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

int main() {
    struct sched_param param;

    // 设置进程的调度策略为 SCHED_FIFO
    if (sched_setscheduler(0, SCHED_FIFO, &param) == -1) {
        perror("sched_setscheduler");
        return 1;
    }

    // 获取当前进程的调度策略和优先级
    int policy = sched_getscheduler(0);
    if (policy == -1) {
        perror("sched_getscheduler");
        return 1;
    }

    int priority = sched_get_priority_max(policy);
    if (priority == -1) {
        perror("sched_get_priority_max");
        return 1;
    }

    printf("调度策略: %d,优先级范围: 1 - %d\n", policy, priority);

    return 0;
}

  在上述示例中,通过调用 sched_setscheduler 函数将当前进程的调度策略设置为 SCHED_FIFO。然后,通过调用 sched_getschedulersched_get_priority_max 函数获取当前进程的调度策略和允许的最高优先级。

二、SCHED_RR 为什么比 SCHED_OTHER 要实时呢

  SCHED_RR 被称为轮转调度(Round-Robin Scheduling)策略,它在 Linux 中被认为是一种实时调度策略。相比于默认调度策略 SCHED_OTHERSCHED_RR 具有实时性的原因有以下几点:

  1. 时间片轮转:SCHED_RR 使用时间片轮转的方式进行调度。每个任务被分配一个时间片,在时间片内运行,当时间片用完时,操作系统会自动切换到下一个任务。这种轮转的调度方式使得每个任务都能按照一定时间间隔获得 CPU 的使用权,提高了实时性。

  2. 优先级调度:SCHED_RR 允许为不同任务设置不同的优先级。优先级高的任务在时间片内先于优先级低的任务执行。这样可以确保优先级高的实时任务能够及时得到 CPU 的调度,并避免优先级低的任务长时间占用 CPU。

  3. 抢占能力:SCHED_RR 具有抢占能力,即优先级更高的任务可以抢占优先级低的任务,即使后者还没有用完时间片。这样,当出现具有更高优先级的实时任务时,操作系统可以及时地将 CPU 资源分配给它,从而提高实时性。

  总体而言,相比于默认的 SCHED_OTHER 调度策略,SCHED_RR 具有更好的实时性能。它为实时应用程序提供了更精确的任务调度控制和保证,使得这些任务能够满足对响应性和实时性要求更高的应用场景。

  需要注意的是,SCHED_RR 并不是 Linux 中唯一的实时调度策略,还有其他实时调度策略如 SCHED_FIFO 可供选择,具体的选择要根据应用需求和系统的实时性要求进行合理的决策。

三、如何使用线程调度相关API

1、相关API介绍

pthread_attr_init
  其是一个 Linux 线程 API 函数,用于初始化线程属性对象。在使用线程属性对象之前,必须先调用该函数进行初始化。

#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);

`attr` 参数是指向线程属性对象的指针,该指针指向的内存空间将被用于存储线程属性信息。

在调用 `pthread_attr_init()` 函数后,`attr` 指向的线程属性对象将被初始化为默认状态,
即使用默认属性值。如果需要更改线程的属性,则可以使用其他线程属性相关的函数(如 
`pthread_attr_setschedpolicy()`、`pthread_attr_setstacksize()` 等)来修改属性值。

函数返回值为 0 表示初始化成功,否则返回相应的错误代码。

pthread_attr_setschedpolicy
  是一个 Linux 线程 API 函数,用于设置线程属性对象(pthread_attr_t)中的调度策略(scheduling policy)。调度策略是指在多个线程竞争 CPU 资源时,操作系统按照一定的规则来分配 CPU 时间片的方式。

#include <pthread.h>
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);

`attr` 参数是指向线程属性对象的指针,而 `policy` 参数则为想要设置的调度策略
。常见的调度策略有以下几种:
	SCHED_FIFO
	SCHED_RR
	SCHED_OTHER

在调用 `pthread_attr_setschedpolicy()` 函数之后,线程所使用的调度策略就被设置为 policy
指定的值。需要注意的是,调度策略只能被设置一次,一旦设置就不能更改。如果需要修改调度策略,
则必须先销毁线程属性对象并重新创建一个新的属性对象。

函数返回值为 0 表示设置成功,否则返回相应的错误代码。

pthread_attr_setstacksize
  其是一个 POSIX 线程库的函数,用于设置线程的堆栈大小。

int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
该函数接受两个参数:
- `attr` 是一个指向线程属性对象的指针。使用 `pthread_attr_init` 初始化线程属性对象,
	并在调用 `pthread_create` 创建线程时传递给它。

- `stacksize` 是一个 `size_t` 类型的值,表示期望的线程堆栈大小,以字节为单位。

该函数返回 0 表示成功,非零值表示失败。

请注意,线程堆栈大小的最小值可能会受到系统和库的限制。在使用时,应该小心选择堆栈大小以
确保适应线程所需的工作负载。

pthread_attr_setaffinity_np
  其是一个 POSIX 线程库函数,在 Linux 平台上设置线程的 CPU 亲和性(CPU affinity)。CPU 亲和性指定了线程允许在哪些 CPU 核心上执行。

int pthread_attr_setaffinity_np(pthread_attr_t *attr, size_t cpusetsize, 
										const cpu_set_t *cpuset);

该函数接受三个参数:
- `attr` 是一个指向线程属性对象(`pthread_attr_t`)的指针。使用 `pthread_attr_init` 
	进行初始化,并在调用 `pthread_create` 时传递给它。

- `cpusetsize` 是一个 `size_t` 类型的值,表示 `cpuset` 参数的大小,以字节为单位。一般
	可以使用 `sizeof(cpu_set_t)` 获取正确的大小。

- `cpuset` 是一个 `cpu_set_t` 类型的指针,表示线程允许执行的 CPU 核心集合。可以使用
	`CPU_ZERO`、`CPU_SET`、`CPU_CLR` 等宏进行设置。

该函数返回 0 表示成功,非零值表示失败。

2、示例代码

  提供一个使用 Linux API 实现线程调度策略、设置线程属性、绑定核心的 C 语言代码示例。下面是一个简单的示例代码:

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

// 线程函数1
void* thread_func1(void* arg) {
    printf("线程1正在运行\n");
    // 线程1的具体任务逻辑
    return NULL;
}

// 线程函数2
void* thread_func2(void* arg) {
    printf("线程2正在运行\n");
    // 线程2的具体任务逻辑
    return NULL;
}

int main() {
    pthread_t thread1, thread2;
    pthread_attr_t attr1, attr2;

    // 初始化线程属性
    pthread_attr_init(&attr1);
    pthread_attr_init(&attr2);

    // 设置线程调度策略:SCHED_FIFO为实时线程,SCHED_OTHER为普通线程
    pthread_attr_setschedpolicy(&attr1, SCHED_FIFO);
    pthread_attr_setschedpolicy(&attr2, SCHED_OTHER);

    // 设置线程栈大小
    size_t stack_size = 1024 * 1024; // 1MB
    pthread_attr_setstacksize(&attr1, stack_size);
    pthread_attr_setstacksize(&attr2, stack_size);

    // 设置线程名称
    pthread_attr_setname(&attr1, "Real-Time Thread");
    pthread_attr_setname(&attr2, "Normal Thread");

    // 创建线程1,绑定到核心0
    cpu_set_t cpuset1;
    CPU_ZERO(&cpuset1);
    CPU_SET(0, &cpuset1);
    pthread_attr_setaffinity_np(&attr1, sizeof(cpu_set_t), &cpuset1);
    pthread_create(&thread1, &attr1, thread_func1, NULL);

    // 创建线程2,绑定到核心1
    cpu_set_t cpuset2;
    CPU_ZERO(&cpuset2);
    CPU_SET(1, &cpuset2);
    pthread_attr_setaffinity_np(&attr2, sizeof(cpu_set_t), &cpuset2);
    pthread_create(&thread2, &attr2, thread_func2, NULL);

    // 等待线程1和线程2运行结束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    // 销毁线程属性
    pthread_attr_destroy(&attr1);
    pthread_attr_destroy(&attr2);

    return 0;
}

  上述代码中,thread_func1()thread_func2() 是分别作为线程1和线程2的函数。通过 pthread_t 数据类型创建线程,并使用 pthread_attr_t 数据类型来设置线程属性。

  在主函数中,我们初始化了线程属性 attr1attr2,并分别为它们设置了调度策略、栈大小、线程名称和绑定的核心。通过调用 pthread_create() 函数创建线程并将线程函数和属性传递给它。

  最后,通过调用 pthread_join() 函数来等待线程1和线程2运行结束,并在程序的末尾销毁线程属性。

  欢迎大家指导和交流!如果我有任何错误或遗漏,请立即指正,我愿意学习改进。期待与大家一起进步!

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小嵌同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值