如何建立一个简单的RT应用程序

翻译官方文档realtime:documentation:howto:applications:application_base [Wiki]

POSIX API构成了在PREEMPT_RT下运行的实时应用程序的基础。对于实时线程,使用POSIX线程(pthread)。每个实时应用程序都需要在调度、优先级、内存锁定和堆栈预置等几个基本领域进行适当的处理。
基本先决条件
接下来的小节将介绍三个基本先决条件,然后是一个简短的例子来说明这些方面。

日程安排和优先级
调度策略和优先级必须由应用程序明确设置。这有两种可能性:
1.使用sched_setscheduler()
在计算RT特定内容之前,需要在pthread的start例程中调用此函数。
2.使用pthread属性
函数pthread_attr_setchedpolicy()和pthread_attr_setschedparam()提供了设置策略和优先级的接口。此外,需要使用PTHREAD_attr_setinheriched()将调度器继承正确设置为PTHREAD_EXPLICIT_SCHED。这迫使新线程使用pthread属性指定的策略和优先级,而不使用

3. 创建实时线程的线程的继承调度。pthread条件变量的问题
依赖glibc的libpthread的多线程应用程序容易出现意外的延迟,因为其条件变量实现不支持优先级继承(bugzilla)。不幸的是,glibc的DNS解析器和异步I/O实现反过来又依赖于这些条件变量。librtpi是另一种LGPL许可的pthread实现,它支持优先级继承,其API尽可能接近glibc。另一种MUSL libc具有类似于glibc的pthread条件变量实现。

内存锁定
看这里
RT线程堆栈
看这里
功能:以非root用户身份以RT优先级运行应用程序
一些Pthread API,如mlockall()、Pthread_attr_setchedpolicy(),默认情况下和约定需要root才能成功完成工作。因此,需要设置RT调度策略和优先级的RT应用程序通常通过sudo运行。

有一个更好的方法;sudo赋予进程root权限。这引起了黑客的兴趣:-)。相反,您应该利用强大的POSIX能力模型!这样,进程(和线程)只获得它们所需的功能,仅此而已。这遵循了infosec的最佳实践,即最小特权原则。
默认情况下,应用程序一开始就没有功能;还要注意,功能是每个线程的资源(本质上转换为任务结构的位掩码,当然是每个线程)。在各种能力位中,能力手册页(7)显示CAP_SYS_NICE是在这种情况下使用的适当能力;功能(7)手册页中的一段代码揭示了这一点:

…CAP_SYS_NICE
降低进程nice值(nice(2),setpriority(2)),并更改任意进程的nice值;
为调用进程设置实时调度策略,并为任意进程设置调度策略和优先级(sched_setscheduler(2)、sched_setparam(2)和sched_setattr(2));
为任意进程设置CPU亲和性(sched_setaffinity(2));
为任意进程设置I/O调度类和优先级(ioprio_set(2));
将migrate_pages(2)应用于任意进程,并允许进程迁移到任意节点;
将move_pages(2)应用于任意进程;
将MPOL_MF_MODE_ALL标志与mbind(2)和MOVE_pages(2)一起使用…

好的,太好了,但如何在应用程序上设置这个功能位呢?
一种方法是通过capget()/capset()系统调用以编程方式进行。(请注意,使用libcap库包装器cap_[g|s]et-proc(3)通常更容易:https://man7.org/linux/man-pages/man3/cap_get_proc.3.html.本手册页甚至提供了这样做的一个小例子)。
另一种简单的方法是利用systemd并将应用程序作为服务运行;在服务单元中,指定功能(请参阅systemd.exec(5)上的手册页;https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Capabilities.
也许最简单的方法是:通过setcap(8)实用程序(它的手册页:https://man7.org/linux/man-pages/man8/setcap.8.html). setcap/getcap通常是libcap包的一部分。例如:
sudo setcap CAP_SYS_NICE+eip<您的应用程序二进制可执行文件>

您可以将此行放入应用程序Makefile(或等效文件)中。(getcap(8)实用程序可用于验证“哑功能”二进制文件现在是否已设置CAP_SYS_NICE位)!
现在,您已经准备好以非root身份运行它,这是一种更安全的方法。

这是一个POSIX实时(Real-Time)线程的示例程序,使用单个POSIX线程 (pthread) 作为实时线程。该程序实现了一个基本的实时线程创建和管理,利用了POSIX线程库和相关的调度策略。下面是对该程序的逐句讲解:

 

c

复制代码

/* * POSIX Real Time Example * using a single pthread as RT thread */

  • 这是程序的注释,表明它是一个POSIX实时线程的示例程序,使用一个pthread作为实时线程。
 

c

复制代码

#include <limits.h> #include <pthread.h> #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h>

  • 包含了一些头文件:
    • limits.h:定义了系统范围的限制和常量。
    • pthread.h:提供了POSIX线程的API。
    • sched.h:用于线程调度相关的定义(例如调度策略)。
    • stdio.h:用于标准输入输出。
    • stdlib.h:提供了常用的库函数(如 exit())。
    • sys/mman.h:用于内存管理(如 mlockall())。
 

c

复制代码

void *thread_func(void *data) { /* Do RT specific stuff here */ return NULL; }

  • 这是线程函数 thread_func,在创建线程后执行的代码。它在当前版本中没有实际功能,只是简单地返回 NULL。用户可以在注释标注的地方插入实时(RT)相关的操作。
 

c

复制代码

int main(int argc, char* argv[]) { struct sched_param param; pthread_attr_t attr; pthread_t thread; int ret;

  • 进入 main() 函数:
    • sched_param param:用于存储线程调度参数(例如优先级)。
    • pthread_attr_t attr:线程属性对象,用于设置线程的各种属性。
    • pthread_t thread:线程ID。
    • int ret:用于存储函数调用的返回值。
 

c

复制代码

/* Lock memory */ if(mlockall(MCL_CURRENT|MCL_FUTURE) == -1) { printf("mlockall failed: %m\n"); exit(-2); }

  • 使用 mlockall() 锁定进程的地址空间,防止其被交换到磁盘上。MCL_CURRENT|MCL_FUTURE 标志表示当前和将来的内存分配都要锁定。
  • 如果锁定失败,打印错误信息并退出程序,返回 -2
 

c

复制代码

/* Initialize pthread attributes (default values) */ ret = pthread_attr_init(&attr); if (ret) { printf("init pthread attributes failed\n"); goto out; }

  • 调用 pthread_attr_init() 初始化线程属性对象 attr,为后续设置调度策略和优先级做准备。如果失败,打印错误信息并跳转到 out 标签结束程序。
 

c

复制代码

/* Set a specific stack size */ ret = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN); if (ret) { printf("pthread setstacksize failed\n"); goto out; }

  • 调用 pthread_attr_setstacksize() 设置线程的栈大小为最小值 PTHREAD_STACK_MIN。如果设置失败,打印错误并跳转到 out 标签。
 

c

复制代码

/* Set scheduler policy and priority of pthread */ ret = pthread_attr_setschedpolicy(&attr, SCHED_FIFO); if (ret) { printf("pthread setschedpolicy failed\n"); goto out; }

  • 使用 pthread_attr_setschedpolicy() 设置线程的调度策略为 SCHED_FIFO,这是实时调度的一种策略,表示先进先出。若失败,打印错误并跳转到 out
 

c

复制代码

param.sched_priority = 80; ret = pthread_attr_setschedparam(&attr, &param); if (ret) { printf("pthread setschedparam failed\n"); goto out; }

  • 设置线程的优先级为 80,并使用 pthread_attr_setschedparam() 应用该优先级。若失败,打印错误并跳转到 out
 

c

复制代码

/* Use scheduling parameters of attr */ ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); if (ret) { printf("pthread setinheritsched failed\n"); goto out; }

  • 使用 pthread_attr_setinheritsched() 设置线程继承调度策略,PTHREAD_EXPLICIT_SCHED 表示显式使用当前设置的调度策略。若失败,打印错误并跳转到 out
 

c

复制代码

/* Create a pthread with specified attributes */ ret = pthread_create(&thread, &attr, thread_func, NULL); if (ret) { printf("create pthread failed\n"); goto out; }

  • 使用 pthread_create() 创建线程,指定 attr 属性,并执行 thread_func。若线程创建失败,打印错误并跳转到 out
 

c

复制代码

/* Join the thread and wait until it is done */ ret = pthread_join(thread, NULL); if (ret) printf("join pthread failed: %m\n");

  • 使用 pthread_join() 等待线程 thread 结束。若失败,打印错误信息。
 

c

复制代码

out: return ret; }

  • 程序结束,返回值为 ret

源代码:

/*                                                                  
 * POSIX Real Time Example
 * using a single pthread as RT thread
 */
 
#include <limits.h>
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
 
void *thread_func(void *data)
{
        /* Do RT specific stuff here */
        return NULL;
}
 
int main(int argc, char* argv[])
{
        struct sched_param param;
        pthread_attr_t attr;
        pthread_t thread;
        int ret;
 
        /* Lock memory */
        if(mlockall(MCL_CURRENT|MCL_FUTURE) == -1) {
                printf("mlockall failed: %m\n");
                exit(-2);
        }
 
        /* Initialize pthread attributes (default values) */
        ret = pthread_attr_init(&attr);
        if (ret) {
                printf("init pthread attributes failed\n");
                goto out;
        }
 
        /* Set a specific stack size  */
        ret = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);
        if (ret) {
            printf("pthread setstacksize failed\n");
            goto out;
        }
 
        /* Set scheduler policy and priority of pthread */
        ret = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
        if (ret) {
                printf("pthread setschedpolicy failed\n");
                goto out;
        }
        param.sched_priority = 80;
        ret = pthread_attr_setschedparam(&attr, &param);
        if (ret) {
                printf("pthread setschedparam failed\n");
                goto out;
        }
        /* Use scheduling parameters of attr */
        ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
        if (ret) {
                printf("pthread setinheritsched failed\n");
                goto out;
        }
 
        /* Create a pthread with specified attributes */
        ret = pthread_create(&thread, &attr, thread_func, NULL);
        if (ret) {
                printf("create pthread failed\n");
                goto out;
        }
 
        /* Join the thread and wait until it is done */
        ret = pthread_join(thread, NULL);
        if (ret)
                printf("join pthread failed: %m\n");
 
out:
        return ret;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值