【问题标题】:Latency spikes while data-logging in real-time embedded Linux在实时嵌入式 Linux 中记录数据时出现延迟峰值
【发布时间】:2016-05-10 14:18:18
【问题描述】:

我有一个机器人在 Beaglebone Black 上的 PREEMPT-RT 修补 Linux 操作系统上运行具有实时优先级的控制代码。所有代码都是用 C 语言编写的,运行频率为 500Hz。

在运行代码时,我经常注意到几百毫秒范围内的延迟,我已将其追踪到我编写的数据记录函数。这种延迟导致我的机器人控制失败,因为我有很多取决于实时功能。

代码的相关部分如下。为了清楚起见,我已经删减了很多代码,但如果需要,我会编辑这篇文章。

FILE *file;

int main(int argc, char** argv) {
    file = fopen(logname, "w");

    while (1) {
        /* Control code stuff*/

        logData();

        time_msec = time_msec + controlLoopTime;
    }
}

void logData() {
    if (time_msec - logTimer_msec >= LOG_TIMER) {
        logTimer_msec = time_msec;

        if (!bLogCreated) {
            fprintf(file,
                    "SensorData1 SensorData2 SensorDataN"
                    );
            bLogCreated = TRUE;
        }

        // log data to file
        fprintf(file,

                "%.2f %.2f\n",

                sensorData1, sensorData2, sensorDataN
        );
    }
}

我需要以良好的速率(可能是 100-125Hz)记录来自多个变量(可能是 20-50)的数据。不需要以控制速率(每 2 毫秒)记录数据,但我已将其减少到 12 毫秒,但我仍然每隔几分钟就会看到延迟峰值。

fprintf 调用可能存在延迟问题。这是 BeagleBone Black、我的代码的限制,还是仅仅是数据记录的性质?

这里提出了一个类似的问题,但似乎没有解决我的问题:Finding latency issues (stalls) in embedded Linux systems

【问题讨论】:

  • 默认情况下,非 tty 文件句柄是块缓冲的。所以在fprintfing 4096 字节之后,缓冲区将被刷新到磁盘,这可能会导致延迟。您不能将实际的日志记录外包给一个单独的线程吗?将日志消息排队并在您的日志线程中出列。
  • 这段代码是否作为正常进程的一部分运行?它是否控制任何会阻塞设备驱动程序的互斥锁、信号量等?
  • @jekso:创建一个日志记录 (p) 线程,并使用双链表之类的东西作为队列(头部入队,尾部出队)和 pthread_cond_wait/notify 来发出新条目的信号。跨度>
  • @wallyk 据我了解您的问题,此代码不会阻止任何设备驱动程序。这是我正在运行的唯一会写入内存的进程。如果需要,我可以发布更多相关的代码部分。
  • 代码是否进行任何其他与系统相关的调用(如 open、close、gettimeofday 等)?我们不需要看逻辑,只看它与系统的交互。显而易见的问题是记录器如何获取它正在记录的数据?

标签: c real-time embedded-linux latency preempt-rt


【解决方案1】:

使用fprintf 会耗费大量时间,尤其是对于 R/T 日志记录。登录二进制并编写一个实用程序以稍后将其打印出来。

代替:

fprintf(file,"%.2f %.2f %.2f",data1,data2,data3);

做:

fwrite(&data1,sizeof(double),1,file);
fwrite(&data2,sizeof(double),1,file);
fwrite(&data3,sizeof(double),1,file);

更好:

struct data {
    double data1;
    double data2;
    double data3;
    time_t event_time;
    ...
};

struct data data;

fwrite(&data,sizeof(struct data),1,file);

如果仍然太慢,请将结构附加到环形队列并让单独的线程写出条目。

如果磁盘写入跟不上 [now] 二进制数据,请维护环形队列,仅在检测到致命错误时才转储队列


另外,请考虑在写入时使用mmap 访问文件。在这里查看我的答案 [with benchmarks]:read line by line in the most efficient way *platform specific*

【讨论】:

  • 这不会解决根本问题,只会将其发生率降低 4 或 10 倍,但仍会发生。执行相同操作的更简单机制是调用 setvbuf() 并分配一个大缓冲区(如 16 兆字节)。
  • 感谢您的回复。所以fwrite 本质上会更快,即使它仍在我的实时进程中间写入文件?编辑:我刚刚在上面看到了 wallyk 的评论,这就是我所关心的。我已经减少了几次写入的数据量,它只是降低了延迟峰值的频率,但并没有完全消除它们。
  • 是的。 fprintf 非常慢(例如慢 100 倍),尤其是浮点数。我已经发布了具有类似日志记录要求的生产 R/T 系统,所以我是根据这里的经验发言的。如果在对文件进行二进制写入更改后,如果仍然太慢,则将数据写入内存环形缓冲区并在检测到错误时转储最后 N 个条目
  • @jekso:这是在什么硬件上运行的?它有浮点硬件吗?
  • @wallyk 这不是磁盘写入。这是fprintf 花费的时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-13
  • 1970-01-01
  • 2021-05-24
  • 2019-02-22
  • 1970-01-01
  • 2018-08-22
  • 2013-05-24
相关资源
最近更新 更多