Bursty写SD / USB在embedded式Linux上拖延我的时间关键型应用程序

我正在开发一个embedded式Linux项目,将ARM9连接到硬件video编码器芯片,并将video写入SD卡或USB存储器。 软件体系结构包括将数据读入缓冲池的内核驱动程序以及将数据写入安装的可移动设备上的文件的用户级应用程序。

我发现超过一定的数据速率(大约750kbyte / sec),我开始看到用户级的video编写应用大概每隔5秒停顿半秒钟。 这足以导致内核驱动程序耗尽缓冲区 – 即使可以增加缓冲区的数量,video数据也必须与其他正在进行的事情同步(理想情况下在40ms内)。 在这5秒的“滞后峰值”之间,写入在40ms内完成(就应用而言 – 我很欣赏它们被操作系统缓冲)

我认为这种滞后现象是与Linux将数据刷新到磁盘的方式有关 – 我注意到pdflush的devise是每5秒唤醒一次,我的理解是,这将是写入的内容。 一旦失速结束,用户级应用程序能够快速服务并写入积压的缓冲区(没有溢出)。

我认为我正在写的设备具有合理的最终吞吐量:从内存fs复制一个15MB的文件,并等待同步完成(并且USB棒的指示灯停止闪烁)给了我大约2.7MBytes / sec的写入速度。

我正在寻找两种线索:

  1. 我怎样才能阻止我的应用程序的突发性写作 – 可能是处理优先级,实时补丁,或调整文件系统代码连续写而不是简单的?

  2. 我怎样才能使我的应用程序知道文件系统发生什么情况,从而在写入数据和卡/棒的吞吐量方面? 我有能力改变硬件编解码器中的video比特率,比丢帧好得多,或者在最大允许的比特率上施加人为上限。

更多信息:这是一个200MHz的ARM9,目前运行的是基于Montavista 2.6.10的内核。

更新:

  • 挂载文件系统SYNC会导致吞吐量太差。
  • 可移动媒体是FAT / FAT32格式的,必须是devise的目的,媒体可以插入任何Windows电脑和阅读。
  • 定期调用sync()或fsync()说,每一秒会导致经常停顿和吞吐量不能接受
  • 我正在使用write()并打开(O_WRONLY | O_CREAT | O_TRUNC)而不是fopen()等
  • 我不能立即在网上find有关上述“Linux实时文件系统”的任何信息。 链接?

我希望这是有道理的。 在stackoverflow上的第一个embedded式Linux问题? 🙂

Solutions Collecting From Web of "Bursty写SD / USB在embedded式Linux上拖延我的时间关键型应用程序"

记录显示,除了最极端的情况外,似乎已经消除了这个问题的两个主要方面。 这个系统还在发展中,还没有经过彻底的酷刑测试,但工作得很好(摸木头)。

用户界面编写器应用程序是多线程的。 有时候写入()该块的调用:其他进程和线程仍然运行。 只要我有一个线程为设备驱动程序提供服务,并更新帧计数和其他数据以与正在运行的其他应用程序同步,数据可以在几秒钟后缓冲并写出,而不会中断任何截止日期。 我尝试了一个简单的乒乓双缓冲区,但这还不够。 小的缓冲区会被压倒,而大的缓冲区会导致更大的暂停,而文件系统会消化写入。 线程之间排队的10个1MB缓冲区的池现在运行良好。

另一方面是关注物理介质的最终写入吞吐量。 为此,我正在关注stat脏:由/ proc / meminfo报告。 我有一些粗糙和准备好的代码来扼杀编码器,如果肮脏:爬上一定的阈值,似乎隐约的工作。 稍后需要更多测试和调整。 幸运的是,我有很多内存(128M)可以玩,给我几秒钟的时间来看看我的积压情况,并顺利进行。

如果我发现我需要做其他事情来处理这个问题,我会尽量记住弹出并更新这个答案。 感谢其他答复者。

我会抛出一些建议,建议很便宜。

  • 确保你使用低级别的API写入磁盘,不要使用用户模式缓存功能,如fopen, fread, fwrite使用较低级别的函数open, read, write
  • 在打开文件时传递O_SYNC标志,这将导致每个写入操作被阻塞,直到写入磁盘,这将消除写入的突发行为…每个写入的开销较慢。
  • 如果您正在从设备读取/ ioctl以获取大量视频数据,则可能需要考虑在应用程序和内核之间分配共享内存区域,否则在传输视频数据缓冲区时会遇到一堆copy_to_user调用从内核空间到用户空间。
  • 您可能需要验证您的USB闪存设备速度是否足够快,并持续传输才能写入数据。

只是一些想法,希望这有助于。

这里有一些关于调优pdflush的写操作的信息。

听起来你正在寻找Linux实时文件系统。 一定要搜索Google等。

XFS有一个实时选项,虽然我没有玩过它。

hdparm可能让你完全关闭缓存。

调整文件系统选项(关闭所有额外的不必要的文件属性)可能会减少你需要刷新,从而加快刷新。 不过,我怀疑这会有多大的帮助。

但我的建议是避免使用棒作为文件系统,而是将其用作原始设备。 东西的数据就像你会用'dd'一样。 然后在别处读取原始数据并在烘烤后写出来。

当然,我不知道这是否是您的选择。

有一个调试帮助,你可以使用strace来查看哪些操作需要花费时间。 FAT / FAT32可能有一些令人惊讶的事情。

你写入一个单一的文件,或在多个文件?

您可以创建一个阅读线程,这将保持一个视频缓冲池准备好写入队列。 当一个帧被接收到时,它被添加到队列中,并且写入线程被发信号通知

共享数据

 empty_buffer_queue ready_buffer_queue video_data_ready_semaphore 

读线程:

 buf=get_buffer() bufer_to_write = buf_dequeue(empty_buffer_queue) memcpy(bufer_to_write, buf) buf_enqueue(bufer_to_write, ready_buffer_queue) sem_post(video_data_ready_semaphore) 

编写线程

 sem_wait(vido_data_ready_semaphore) bufer_to_write = buf_dequeue(ready_buffer_queue) write_buffer buf_enqueue(bufer_to_write, empty_buffer_queue) 

如果你的写作线程被阻塞,等待内核,这可能工作。 然而,如果你被封锁在kerne空间内,那么除了寻找一个比2.6.10更新的内核外,没有什么可以做的了。

不知道更多关于你的具体情况,我只能提供以下的猜测:

尝试使用fsync()/ sync()来强制内核更频繁地刷新数据到存储设备。 这听起来像内核缓冲所有的写入,然后绑定总线或在执行实际写入时停止系统。 仔细调用fsync(),您可以尝试以更细粒度的方式在系统总线上安排写入操作。

编码/捕捉(你没有提到视频捕捉,所以我在这里做一个假设 – 你可能想要添加更多的信息)任务运行在自己的线程中,缓冲它的输出在用户空间 – 然后,第二个线程可以处理写入设备。 这将给你一个平滑的缓冲区,让编码器总是完成其写入没有阻塞。

有一件听起来很可疑的事情是,你只能以某种数据速率看到这个问题 – 如果这真的是一个缓冲问题,我预计这个问题在较低数据速率下发生的频率较低,但我仍然期望看到问题。

无论如何,更多的信息可能是有用的。 什么是您的系统架构? (非常笼统地说)

鉴于您提供的附加信息,听起来就像设备的吞吐量对于小写入和频繁刷新而言相当糟糕。 如果您确信对于较大的写入,您可以获得足够的吞吐量(我不确定是这种情况,但文件系统可能会做一些愚蠢的事情,比如每次写入后更新FAT),然后使用编码线程管道数据写入线程中具有足够的缓冲以避免停顿。 过去我使用共享内存环形缓冲区来实现这种方案,但是除非缓冲区已满,否则允许写入器写入I / O进程而没有停顿的任何IPC机制都可以实现。

一个有用的Linux函数和同步或fsync的替代是sync_file_range。 这使您可以调度数据进行写入,而无需等待内核内缓冲区系统解决该问题。

为了避免长时间的停顿,请确保您的IO队列(例如:/ sys / block / hda / queue / nr_requests)足够大。 该队列是数据从内存刷新到磁盘到达的地方。

请注意,sync_file_range不是可移植的,只能在内核2.6.17及更高版本中使用。

我已经被告知主机发送命令后,MMC卡和SD卡“必须在0到8字节内响应”。

然而,规格允许这些卡在“完成”操作之前以“忙”的方式进行响应,显然对于卡可以占用多长时间是没有限制的(请告诉我是否有这样的限制)。

我发现一些低成本的闪存芯片,如M25P80,保证了“最大单扇区擦除时间”为3秒,尽管通常它“仅”需要0.6秒。

那0.6秒听起来有点类似于你的“半秒钟停顿”。

我怀疑廉价,慢速闪存芯片和昂贵的快闪芯片之间的折衷与USB闪存驱动器结果的广泛变化有关:

我听说有传闻说,每当闪存扇区被擦除然后重新编程时,它会比上一次更长一点。

所以,如果你有一个时间关键的应用程序,你可能需要(a)测试你的SD卡和USB记忆棒,以确保它们满足你的应用程序所要求的最小延迟,带宽等,(b)按照惯例重新测试或先发制人地替换这些存储设备。

显而易见的第一,你有没有明确告诉文件冲洗? 我也觉得可能有些ioctl可以用来做,但是我真的没有做太多的C / POSIX文件编程。

看到你在一个Linux内核上,你应该能够调整和重建内核,使之更适合你的需求,例如。 更频繁,但也是较小的冲洗永久存储。


在我的手册页快速检查发现:

  SYNC(2)Linux程序员手册SYNC(2)

名称
        sync  - 将缓冲区缓存提交到磁盘

概要
        #include <unistd.h>

        void sync(void);

    glibc的功能测试宏要求(请参阅feature_test_macros(7)):

        sync():_BSD_SOURCE ||  _XOPEN_SOURCE> = 500

描述
        sync()首先将inode提交到缓冲区,然后缓冲到磁盘。

错误
       这个功能总是成功的。 

做你自己的冲洗()ing听起来对我来说 – 你想控制,不要把它放在通用缓冲层的变幻莫测。

这可能是显而易见的,但要确保不要太频繁地调用write() – 确保每个write()都有足够的数据写入,以使系统调用的开销值得。 而且,在另一个方向上,不要很少叫它,否则它会阻塞足够长的时间来引起一个问题。

在一个更难以实现的轨道上,你尝试过切换到异步I / O吗? 使用aio,你可以开始写一个缓冲区,当你将视频数据吸入另一个缓冲区时,你可以交换一组缓冲区。