Linux 内核实时补丁 PREEMPT_RT补丁 与 Linux4.1.15 + patch-4.1.15-rt18.patch 补丁,实测实时性差

本文探讨了在Linux4.1.15与3.14.52环境下,使用preempt_rt补丁实现实时性的差异。面对实时性不佳的问题,文章详细记录了排查与解决过程,包括补丁应用、内核配置、测试程序对比及实时线程创建方法。

Linux4.1.15 + patch-4.1.15-rt18.patch 补丁,实测实时性差

开发环境

发行环境
该环境用于发行
Linux: 4.1.15
preempt_rt: patch-4.1.15-rt18.patch
SOC: i.MX6Q
board: 飞凌OK_MX6X_C_V1.3
运行程序: main (最终部署程序)
开发环境
在开发阶段使用的该环境
Linux: 3.14.52
preempt_rt: patch-3.14.51-rt52.patch
SOC: i.MX6Q
board: LYS-IMX6Q
测试程序:test (实时测试程序)

测试环境

测试环境
使用Keysight MSOX4034A示波器进行测试, 探头一接入网络数据输入口RGMII_RXD0, 探头二接入CAN数据输出口TXD; 触发方式采用依次按边沿触发, 当1通道触发4ns后2通道触发即触发; 示波器水平时间/格为1ms; 数据输入周期为100ms/10Hz.
测试程序
创建服务器端socket套接字接受客户端发送过来的指令, 当接受到指令后, 立即向CAN总线发送一帧数据

目标

发行环境中搭建起Linux实时环境并运行程序,实测达到实时性要求(抖动在200us内)
开发环境中已经实现目标,现需要迁移到发行环境

问题

在10000次的测试中,平均延时225.63us;最小延时207.0us;最大延时7.0243ms;计数值低于实际发送值
其最大延时远超预期,不是实时操作系统的结果。而且在随着示波器水平时间的增大最大延时也随之增大。可以推出:在测试过程中有在示波器当前配置的检测范围外的数据。


解决过程

我仔细梳理了以前的测试过程,结果,以及现在测试的过程,结果。首先明确了并再次证实了以前测试的结果是正确的,通过打补丁,测试实时进程,测试方法这一系列流程都是正确的。并在之前的板子是验证了。但现在的这块飞凌板不行。问题出在哪了?

仔细与上一个开发板的步骤与环境进行对比,得出如下结论:
补丁文件正确,打补丁无误,针对补丁的内核配置无误,测试方法无误,程序交叉测试也正常运行。(消耗2天时间)
那这过程中所有步骤都正确无误,那又是什么问题导致了呢。

既然过程中的步骤无误,那就从过程中不同点出发,不同点如下:
1.Linux内核版本不同
2.preempt_rt版本不同,跟随内核版本变化
3.设备树不同
4.根文件系统不同
5.测试程序不同
遂针对以上不同点进行排查。
首先在经过验证的。然后是根文件系统,安装了两个不同的根文件系统,运行相同程序,实验结果无实时。说明根文件系统的不同对其无影响。
然后是测试程序不同,将test/main运行在开发环境,最大延时200us左右,抖动在100us以内,说明程序中实时性操作步骤对于开发环境是正确的。然而将test/main运行在发行环境,最大延时都在ms级;当时就联想到了会不会是Linux内核版本不同所以实时进程的操作方式会不同呢。翻阅了很多资料,其中最有效的链接如下:
Real-Time Linux Wiki
HOWTO: RTOS and RT Applications
参照其中创建RT线程的步骤,顺利解决了我的问题。后面我将结合我之前的操作步骤以及链接中的内容。给出我的实现步骤

Linux + PREEMPT_RT 实现步骤

1.参照HOWTO setup Linux with PREEMPT_RT properly该链接下载补丁,并打上补丁,配置内核,最后编译内核

2.步骤1中的编译环节如果出现问题。请参考如下修改:

1.include/linux/imx_sema4.h
+#include <linux/wait-simple.h>     +20
-       wait_queue_head_t *wqp;     +43
+       struct swait_head *wqp;

2.drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c  +7365/+7402/+7430
-			spin_lock_irq(&x->wait.lock);
+			raw_spin_lock_irq(&x->wait.lock);
-           spin_unlock_irq(&signal->obj.wait.lock);
+           raw_spin_unlock_irq(&signal->obj.wait.lock);
总共有4处

3.drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c  +7390
-           DECLARE_WAITQUEUE(wait, current);
-           wait.flags |= WQ_FLAG_EXCLUSIVE;
-           __add_wait_queue_tail(&signal->obj.wait, &wait);
+           DEFINE_SWAITER(wait);
+           swait_prepare_locked(&signal->obj.wait, &wait);
3.drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_os.c  +7429
-           __remove_wait_queue(&signal->obj.wait, &wait);
+           swait_finish_locked(&signal->obj.wait, &wait);

3.编译好内核后,进行烧录,并启动。启动好的系统运行程序,并不是实时的。需要程序去控制实时。
Linux 3.14.52版本中,我的操作方式如下

int process_set_rt(void)
{
    /* 设置进程为实时进程, 提高优先级 */
    pid_t pid = getpid();
    struct sched_param param;
    param.sched_priority = sched_get_priority_max(SCHED_FIFO); // 也可用SCHED_RR
    sched_setscheduler(pid, SCHED_FIFO, &param);                 // 设置当前进程
    return 0;
}

然后在你需要实时的进程运行业务前执行**process_set_rt()**函数。经过实测,该进程创建的线程也是实时的。

Linux 4.1.15版本中,我的操作方式如下

int create_rt_thread(pthread_t* thread, void *(*thread_func)(void))
{
    struct sched_param param;
    pthread_attr_t attr;
    int ret;

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

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

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

    /* Set scheduler policy and priority of pthread */
    ret = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
    if(ret) {
        printf("pthread setschedpolicy failed\n");
        return -1;
    }
    param.sched_priority = 80;
    ret = pthread_attr_setschedparam(&attr, &param);
    if (ret) {
        printf("pthread setschedparam failed\n");
        return -1;
    }
    /* Use scheduling parameters of attr */
    ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
    if (ret) {
        printf("pthread setinheritsched failed\n");
        return -1;
    }

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

对于需要实时的线程,用**create_rt_thread()**函数创建实时线程。
注意:

Keep in mind that the usual sequence is for an application to begin its execution as a regular (non-RT) application, then create the RT threads with appropriate resources and scheduling parameters.

意思就是说:创建实时线程的进程需是非实时进程。

以上是我对实时补丁的理解,如有错误欢迎指正,同时期待猿友给出为什么在Linux3.14.52中能实时的操作,在Linux4.1.15中却无法实时。期待你们的答案,谢谢!

附上我解决问题中参考的链接
Linux内核Kconfig语法
PREEMPT_RT下载地址
关于Linux Preempt RT补丁的实时编程问题?
Linux内核配置解析 - 概述(基于ARM64架构)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值