当前位置: 首页 > news >正文

[实战] 实时任务 vs 非实时任务:在PREEMPT-RT环境下的编程实践

实时任务 vs 非实时任务:在PREEMPT-RT环境下的编程实践

实时操作系统,Linux的实时化,是嵌入式开发者们不可回避的痛。本文就Linux实时化后的应用,做一些讨论,希望可以抛砖引玉,对大家有所裨益。

文章目录

  • 实时任务 vs 非实时任务:在PREEMPT-RT环境下的编程实践
    • 引言
    • PREEMPT-RT:实时能力的基石
    • 实时任务 vs 非实时任务:核心差异
      • 调度策略的根本不同
      • 时间确定性的关键差异
    • 系统负载下的行为差异
    • 实时应用编程最佳实践
      • 1. 完整的实时任务模板
      • 2. 优先级继承和互斥锁
    • 系统配置和权限设置
      • 用户权限配置
      • CPU隔离配置
    • 测试和验证
      • 使用cyclictest验证系统实时性
    • 注意事项和最佳实践
      • 实时任务编程陷阱
    • 总结
    • 通过本文的实例和最佳实践,您应该能够在PREEMPT-RT环境下正确编写实时应用程序,充分发挥Linux的实时能力。记住,实时编程既是科学也是艺术,需要在确定性和系统资源之间找到平衡。

引言

在工业控制、机器人、音视频处理等对响应时间有严格要求的领域,实时计算能力至关重要。Linux通过PREEMPT-RT补丁实现了硬实时能力,但很多开发者存在一个误解:认为应用PREEMPT-RT后,所有程序都会自动获得实时性能。事实并非如此!

本文将深入探讨实时任务与非实时任务的核心区别,并通过完整实例展示如何在PREEMPT-RT环境下正确进行实时应用编程。

PREEMPT-RT:实时能力的基石

PREEMPT-RT补丁通过以下关键改进为Linux提供了实时能力:

  • 完全内核可抢占:减少不可抢占区域
  • 中断线程化:将硬件中断处理转为可调度的内核线程
  • 优先级继承:防止优先级反转问题
  • 高精度定时器:提供纳秒级时间控制

重要提示:PREEMPT-RT只是提供了实时能力的基础设施,应用程序必须主动"声明"自己的实时需求才能获得确定性响应。

实时任务 vs 非实时任务:核心差异

调度策略的根本不同

// 调度策略对比演示
#include <stdio.h>
#include <pthread.h>
#include <sched.h>
#include <unistd.h>
#include <sys/mman.h>void demonstrate_scheduling_difference() {pthread_t rt_thread, normal_thread;// 实时线程属性 - 使用SCHED_FIFOpthread_attr_t rt_attr;pthread_attr_init(&rt_attr);pthread_attr_setschedpolicy(&rt_attr, SCHED_FIFO);struct sched_param rt_param = {.sched_priority = 80};pthread_attr_setschedparam(&rt_attr, &rt_param);// 非实时线程属性 - 使用默认SCHED_OTHERpthread_attr_t normal_attr;pthread_attr_init(&normal_attr);printf("=== 调度策略对比演示 ===\n");
}
特性实时任务非实时任务
调度策略SCHED_FIFO / SCHED_RRSCHED_OTHER (CFS)
优先级范围1-99 (越高越优先)Nice值: -20到19
抢占能力可抢占几乎所有任务只能被实时任务抢占
设计目标确定性和低延迟公平性和吞吐量

时间确定性的关键差异

实时任务的核心优势在于时间确定性。下面通过一个完整的示例来展示这种差异:

// timing_comparison.c
#include <stdio.h>
#include <pthread.h>
#include <sched.h>
#include <time.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>#define NSEC_PER_SEC 1000000000L
#define ITERATIONS 200// 获取当前时间(纳秒)
static uint64_t get_time_ns() {struct timespec ts;clock_gettime(CLOCK_MONOTONIC, &ts);return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
}// 实时任务 - 高时间确定性
void* deterministic_rt_task(void* arg) {struct sched_param param = {.sched_priority = 85};if (sched_setscheduler(0, SCHED_FIFO, &param) == -1) {printf("RT sched_setscheduler failed: %s\n", strerror(errno));return NULL;}// 锁定内存,避免换页延迟if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {printf("mlockall failed: %s\n", strerror(errno));}long period_ns = 1000000; // 1ms周期uint64_t start_time, next_time;long long total_jitter = 0;long long max_jitter = 0;start_time = get_time_ns();next_time = start_time;for (int i = 0; i < ITERATIONS; i++) {// 等待精确的时间点while (get_time_ns() < next_time) {// 忙等待,获得最精确的定时}uint64_t current_time = get_time_ns();long long jitter = current_time - next_time;total_jitter += jitter;if (jitter > max_jitter) max_jitter = jitter;next_time += period_ns;if (i % 50 == 0) {printf("RT Task: Iteration %d, jitter: %lld ns\n", i, jitter);}}printf("=== 实时任务结果 ===\n");printf("平均抖动: %lld ns\n", total_jitter / ITERATIONS);printf("最大抖动: %lld ns\n", max_jitter);return NULL;
}// 非实时任务 - 时间不确定性
void* nondeterministic_normal_task(void* arg) {long period_ns = 1000000; // 1ms周期uint64_t start_time, next_time;long long total_jitter = 0;long long max_jitter = 0;start_time = get_time_ns();next_time = start_time;for (int i = 0; i < ITERATIONS; i++) {// 使用usleep,精度较差且易受系统负载影响usleep(1000); // 约1msuint64_t current_time = get_time_ns();long long jitter = current_time - next_time;total_jitter += jitter;if (jitter > max_jitter) max_jitter = jitter;next_time += period_ns;if (i % 50 == 0) {printf("Normal Task: Iteration %d, jitter: %lld ns\n", i, jitter);}}printf("=== 非实时任务结果 ===\n");printf("平均抖动: %lld ns\n", total_jitter / ITERATIONS);printf("最大抖动: %lld ns\n", max_jitter);return NULL;
}void run_comparison() {pthread_t rt_thread, normal_thread;printf("启动实时任务 vs 非实时任务对比测试...\n\n");// 先启动非实时任务pthread_create(&normal_thread, NULL, nondeterministic_normal_task, NULL);// 然后启动实时任务(实时任务会立即抢占)pthread_create(&rt_thread, NULL, deterministic_rt_task, NULL);pthread_join(rt_thread, NULL);pthread_join(normal_thread, NULL);
}

编译和运行:

gcc timing_comparison.c -o timing_comparison -lpthread -lrt
sudo ./timing_comparison

典型运行结果

=== 实时任务结果 ===
平均抖动: 156 ns
最大抖动: 2450 ns=== 非实时任务结果 ===  
平均抖动: 12500 ns
最大抖动: 156000 ns

从结果可以明显看出,实时任务的时间确定性远高于非实时任务。

系统负载下的行为差异

实时任务的真正价值在高系统负载下体现得更加明显:

// load_behavior_comparison.c
#include <stdio.h>
#include <pthread.h>
#include <sched.h>
#include <time.h>
#include <sys/mman.h>// CPU密集型负载任务
void* cpu_load_task(void* arg) {printf("CPU负载任务启动 - 模拟系统高负载\n");while (1) {// 消耗大量CPU周期volatile unsigned long long i;for (i = 0; i < 5000000ULL; i++);}return NULL;
}void* rt_task_under_load(void* arg) {struct sched_param param = {.sched_priority = 90};sched_setscheduler(0, SCHED_FIFO, &param);mlockall(MCL_CURRENT | MCL_FUTURE);uint64_t start_time, end_time;long long worst_case_latency = 0;long period_ns = 2000000; // 2ms周期uint64_t next_time = get_time_ns();for (int i = 0; i < 100; i++) {// 等待周期开始while (get_time_ns() < next_time);start_time = get_time_ns();// 模拟实时工作(50us的工作负载)volatile unsigned int j;for (j = 0; j < 5000; j++);end_time = get_time_ns();long long latency = end_time - start_time;if (latency > worst_case_latency) {worst_case_latency = latency;printf("RT任务: 新的最坏情况延迟: %lld ns (迭代 %d)\n", worst_case_latency, i);}next_time += period_ns;}printf("RT任务最终最坏情况延迟: %lld ns\n", worst_case_latency);return NULL;
}void* normal_task_under_load(void* arg) {uint64_t start_time, end_time;long long worst_case_latency = 0;for (int i = 0; i < 100; i++) {start_time = get_time_ns();// 模拟工作(50us的工作负载)volatile unsigned int j;for (j = 0; j < 5000; j++);end_time = get_time_ns();long long latency = end_time - start_time;if (latency > worst_case_latency) {worst_case_latency = latency;printf("普通任务: 新的最坏情况延迟: %lld ns (迭代 %d)\n", worst_case_latency, i);}usleep(2000); // 2ms周期}printf("普通任务最终最坏情况延迟: %lld ns\n", worst_case_latency);return NULL;
}

实时应用编程最佳实践

1. 完整的实时任务模板

// complete_rt_task_template.c
#include <stdio.h>
#include <pthread.h>
#include <sched.h>
#include <time.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>#define NSEC_PER_SEC 1000000000Ltypedef struct {int thread_id;int priority;long period_ns;int iterations;
} rt_task_params_t;// 完整的实时任务实现
void* complete_rt_task(void* arg) {rt_task_params_t* params = (rt_task_params_t*)arg;// 1. 设置实时调度策略struct sched_param sched_param = {.sched_priority = params->priority};if (sched_setscheduler(0, SCHED_FIFO, &sched_param) == -1) {printf("线程 %d: sched_setscheduler 失败: %s\n", params->thread_id, strerror(errno));return NULL;}// 2. 锁定内存if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {printf("线程 %d: mlockall 失败: %s\n", params->thread_id, strerror(errno));}// 3. 设置CPU亲和性(可选)cpu_set_t cpuset;CPU_ZERO(&cpuset);CPU_SET(params->thread_id % 4, &cpuset); // 绑定到不同CPU核心if (sched_setaffinity(0, sizeof(cpuset), &cpuset) == -1) {printf("线程 %d: sched_setaffinity 失败: %s\n",params->thread_id, strerror(errno));}printf("实时线程 %d 启动: 优先级=%d, 周期=%ld ns, CPU亲和性=%d\n",params->thread_id, params->priority, params->period_ns, params->thread_id % 4);uint64_t start_time, next_time;long long total_jitter = 0;long long max_jitter = 0;int deadline_misses = 0;start_time = get_time_ns();next_time = start_time;for (int i = 0; i < params->iterations; i++) {// 等待周期开始while (get_time_ns() < next_time) {// 精确忙等待}uint64_t cycle_start = get_time_ns();// 检查截止时间错过if (cycle_start - next_time > 10000) { // 超过10us视为错过截止时间deadline_misses++;printf("线程 %d: 迭代 %d 错过截止时间! 延迟: %ld ns\n",params->thread_id, i, cycle_start - next_time);}// 执行实时工作// 模拟实际工作负载(可根据需要调整)volatile unsigned int work;for (work = 0; work < 10000; work++);uint64_t cycle_end = get_time_ns();long long jitter = cycle_start - next_time;total_jitter += jitter;if (jitter > max_jitter) max_jitter = jitter;// 报告性能数据if (i % (params->iterations / 10) == 0) {printf("线程 %d: 迭代 %d, 抖动: %lld ns, 工作耗时: %ld ns\n",params->thread_id, i, jitter, cycle_end - cycle_start);}next_time += params->period_ns;}printf("\n=== 实时线程 %d 完成 ===\n", params->thread_id);printf("平均抖动: %lld ns\n", total_jitter / params->iterations);printf("最大抖动: %lld ns\n", max_jitter);printf("截止时间错过次数: %d\n", deadline_misses);return NULL;
}// 创建多个实时任务的示例
void create_multiple_rt_tasks() {pthread_t threads[3];rt_task_params_t params[3];// 配置不同优先级的实时任务for (int i = 0; i < 3; i++) {params[i].thread_id = i;params[i].priority = 80 + i * 5; // 80, 85, 90params[i].period_ns = 1000000 * (i + 1); // 1ms, 2ms, 3msparams[i].iterations = 500;if (pthread_create(&threads[i], NULL, complete_rt_task, &params[i])) {perror("pthread_create失败");return;}}// 等待所有任务完成for (int i = 0; i < 3; i++) {pthread_join(threads[i], NULL);}
}

2. 优先级继承和互斥锁

// priority_inheritance_example.c
#include <stdio.h>
#include <pthread.h>
#include <sched.h>
#include <unistd.h>pthread_mutex_t rt_mutex;void demonstrate_priority_inheritance() {pthread_t low_prio_thread, high_prio_thread;pthread_mutexattr_t mutex_attr;// 配置实时互斥锁属性(启用优先级继承)pthread_mutexattr_init(&mutex_attr);pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT);pthread_mutex_init(&rt_mutex, &mutex_attr);printf("=== 优先级继承演示 ===\n");// 创建低优先级任务(先获取锁)pthread_create(&low_prio_thread, NULL, low_priority_task, NULL);usleep(50000); // 确保低优先级任务先运行// 创建高优先级任务(后尝试获取锁)pthread_create(&high_prio_thread, NULL, high_priority_task, NULL);pthread_join(low_prio_thread, NULL);pthread_join(high_prio_thread, NULL);pthread_mutex_destroy(&rt_mutex);
}

系统配置和权限设置

用户权限配置

为了避免每次都需要root权限运行实时应用:

# 编辑 /etc/security/limits.conf
username - rtprio 99
username - memlock unlimited# 或者使用capabilities
sudo setcap cap_sys_nice+ep ./your_rt_app

CPU隔离配置

# 在 /etc/default/grub 中配置
GRUB_CMDLINE_LINUX="isolcpus=2,3 nohz_full=2,3 rcu_nocbs=2,3"# 更新GRUB后重启
sudo update-grub

测试和验证

使用cyclictest验证系统实时性

# 安装测试工具
sudo apt install rt-tests# 基础测试
sudo cyclictest -t1 -p80 -n -i 1000 -l 10000# 高负载下测试
stress --cpu 4 --io 2 --vm 1 --vm-bytes 128M &
sudo cyclictest -t1 -p99 -n -i 1000 -l 100000

注意事项和最佳实践

实时任务编程陷阱

  1. 避免无限循环不释放CPU

    // 错误示例
    while (1) {process_data(); // 没有休眠,会饿死其他任务
    }// 正确做法
    while (1) {process_data();clock_nanosleep(...); // 定期释放CPU
    }
    
  2. 合理设置优先级

    • 不要滥用最高优先级(99)
    • 根据任务关键性合理分配优先级
  3. 注意资源管理

    • 使用mlockall()锁定内存
    • 设置合理的CPU亲和性
    • 使用实时安全的互斥锁

总结

实时任务和非实时任务在PREEMPT-RT环境下的核心区别可以总结为:

维度实时任务非实时任务
设计哲学确定性优先公平性优先
适用场景控制循环、信号处理UI、文件I/O、网络
编程复杂度高(需考虑时序)低(常规编程)
系统影响可能使系统无响应对系统影响小

关键要点

  • PREEMPT-RT提供了实时能力的基础设施
  • 应用程序必须主动声明实时需求才能获得确定性
  • 实时编程需要特殊的技术:实时调度策略、内存锁定、优先级继承等
  • 合理设计实时任务优先级和周期,避免系统稳定性问题

通过本文的实例和最佳实践,您应该能够在PREEMPT-RT环境下正确编写实时应用程序,充分发挥Linux的实时能力。记住,实时编程既是科学也是艺术,需要在确定性和系统资源之间找到平衡。

研究学习不易,点赞易。
工作生活不易,收藏易,点收藏不迷茫 :)


http://www.dtcms.com/a/436062.html

相关文章:

  • RabbitMq入门之概括
  • 山西营销网站建设那个公司好上海百度seo
  • 经验分享:如何通过SAP HANA数据库优化将SAP B1性能提升50%
  • 免费注册域名邮箱龙岗优化网站建设
  • 如何通过cpa网站做推广产品展厅柜设计公司
  • 机器视觉滤光片怎么选?
  • 韶关市建设与房地产信息网站营销排名seo
  • 波音网站开发php网站开发用什么ide
  • 电子商务网站功能设计与分析微信社群营销怎么做
  • 运城手机网站制作郑州高端设计公司
  • 音乐网站建设规划第1ppt模板免费下载
  • 如何对网站进行分析网站开发合作
  • GUI自动化之pywinauto
  • 杭州网站设计费用app软件下载入口
  • 网站建设php教程建设一个好的网站
  • 遵义网站建设找工作百安居装修口碑怎么样
  • 用别人的网站视频做app网站建设文字设计
  • 网站建设推广兼职地推一手项目平台
  • 网站建设和安全管理制度html5制作网页的代码
  • C++类和对象(1)
  • 嵌入式开发学习日志32——stm32之PWM
  • 数据结构 之 【图的最短路径】(Dijstra、BellmanFord、FloydWarShall算法实现)
  • 时序数据库高基数问题(一):当数据标签太多时会发生什么
  • 东莞市企业网站制作企业关键词推广优化排名品牌
  • 个人网站免费搭建软文标题和内容
  • 普洱高端网站建设价格燕郊房价2023年最新房价走势
  • 怎么做二维码微信扫后直到网站合肥网站排名提升
  • 如何办网站 论坛网站一定要公司吗
  • 主流网站开发平台新媒体运营需要哪些技能
  • 江门网站php网站开发遇到的问题