当前位置:首页> 正文

关于视频:Bursty向SD / USB写入内容使我的时间紧迫的应用程序停滞在嵌入式Linux上

关于视频:Bursty向SD / USB写入内容使我的时间紧迫的应用程序停滞在嵌入式Linux上

Bursty writes to SD/USB stalling my time-critical apps on embedded Linux

我正在从事一个嵌入式Linux项目,该项目将ARM9连接到硬件视频编码器芯片,并将视频写到SD卡或USB记忆棒中。该软件体系结构包括将数据读入缓冲池的内核驱动程序,以及将数据写入已安装的可移动设备上的文件的userland应用程序。

我发现高于一定数据速率(约750kbyte / sec)时,我开始看到userland视频编写应用程序停顿了大约半秒钟,大约每5秒钟一次。这足以导致内核驱动程序用尽缓冲区-即使我可以增加缓冲区的数量,视频数据也必须与实时进行的其他事情同步(理想情况下是在40毫秒之内)。在这5秒的"滞后尖峰"之间,写入在40毫秒内完成得很好(就应用程序而言-我很欣赏它们被操作系统缓冲了)

我认为这种滞后峰值与Linux将数据刷新到磁盘的方式有关-我注意到pdflush旨在每5s唤醒一次,我的理解是这将是写作的目的。一旦停滞结束,userland应用程序便能够快速服务并编写缓冲区的积压(未溢出)。

我认为我要写入的设备具有合理的最终吞吐量:从内存fs复制15MB文件并等待同步完成(并且USB记忆棒的光停止闪烁)使我的写入速度约为2.7MBytes /秒。

我正在寻找两种线索:

  • 如何阻止突发写入停止我的应用程序-可能处理优先级,实时补丁或调整文件系统代码以连续写入而不是突发写入?

  • 如何在写入积压和卡/棒的吞吐量方面使我的应用程序了解文件系统的状况?我能够即时更改硬件编解码器中的视频比特率,这比丢弃帧或对最大允许比特率施加人为上限要好得多。

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

    更新:

    • 挂载文件系统SYNC会导致吞吐量太差。

    • 可移动媒体是FAT / FAT32格式的,并且设计时必须确保该媒体可以插入任何Windows PC并读取。

    • 定期调用sync()或fsync()表示,每秒都会导致常规停顿和不可接受的吞吐量下降

    • 我正在使用write()和open(O_WRONLY | O_CREAT | O_TRUNC)而不是fopen()等。

    • 我无法立即在线找到有关上述" Linux实时文件系统"的任何信息。链接?

    我希望这是有道理的。关于stackoverflow的第一个嵌入式Linux问题? :)


    从记录来看,除了最极端的情况以外,似乎有两个主要方面消除了该问题。该系统仍在开发中,尚未经过严格的折磨测试,但运行良好(触摸木头)。

    最大的胜利来自使userland writer应用程序成为多线程。有时会阻塞对write()的调用:其他进程和线程仍在运行。只要我有一个线程为设备驱动程序提供服务,并更新帧计数和其他数据以与正在运行的其他应用程序同步,就可以在几秒钟后对数据进行缓冲和写出,而不会中断任何截止日期。我先尝试了一个简单的乒乓双缓冲,但这还不够;小缓冲区将不堪重负,而大缓冲区只会在文件系统消化写入操作时造成较大的暂停。现在,在线程之间排队的10个1MB缓冲区池可以正常工作。

    另一方面是关注物理介质的最终写入吞吐量。为此,我一直在关注stat Dirty:由/ proc / meminfo报告。如果"脏",我有一些粗糙的现成的代码可以限制编码器:爬到某个阈值之上,似乎可以正常工作。以后需要更多测试和调整。幸运的是,我有很多RAM(128M)可以玩,给了我几秒钟的时间来查看积压的积压和平稳地减少油压。

    如果我发现需要采取其他措施来解决此问题,我会尽量记住弹出并更新此答案。感谢其他答复者。


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

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

    只需几个想法,希望对您有所帮助。


    这是一些有关为大量写操作调整pdflush的信息。


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

    XFS具有实时选项,尽管我没有玩过。

    hdparm可能会让您完全关闭缓存。

    调整文件系统选项(关闭所有其他不需要的文件属性)可能会减少需要刷新的内容,从而加快了刷新速度。我怀疑这是否会有所帮助。

    但是我的建议是完全避免将记忆棒用作文件系统,而应将其用作原始设备。像使用\\'dd \\'一样填充数据。然后在其他地方读取该原始数据,并在烘烤后将其写出。

    当然,我不知道这是否适合您。


    有人告诉我,主机发送命令后,MMC和SD卡"必须在0到8个字节内响应"。

    但是,规范允许这些卡以"忙"状态响应,直到完成操作为止,并且显然对卡可以声称处于忙状态的时间没有限制(请告诉我是否有忙这样的限制)。

    我看到某些低成本的闪存芯片(例如M25P80)具有"最大单扇区擦除时间"保证的3秒,尽管通常"仅"需要0.6秒。

    0.6秒的声音听起来与您的"大概呆了半秒"类似。

    我怀疑廉价,慢速闪存芯片与昂贵,快速闪存芯片之间的权衡与USB闪存驱动器结果的广泛差异有关:

    • http://www.testfreaks.com/blog/information/16gb-usb-drive-comparison-17-drives-compared/
    • http://www.tomshardware.com/reviews/data-transfer-run,1037-10.html

    我听说有传言说,每次擦除闪存扇区然后对其进行重新编程时,都需要比上次更长的时间。

    因此,如果您有时间紧迫的应用程序,则可能需要(a)测试SD卡和USB记忆棒,以确保它们满足应用程序所需的最小延迟,带宽等,以及(b)定期重新测试或抢先更换这些存储设备。


    sync_file_range是有用的Linux功能,并且是sync或fsync的替代方法。这样一来,您就可以调度要写入的数据,而无需等待内核缓冲系统熟悉它。

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

    请注意,sync_file_range不可移植,并且仅在内核2.6.17及更高版本中可用。


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

    您要写入单个文件还是多个文件?

    您可以创建一个读取线程,该线程将维护准备写入队列的视频缓冲池。
    接收到帧后,将其添加到队列中,并向写线程发信号通知

    共享数据

    1
    2
    3
    empty_buffer_queue
    ready_buffer_queue
    video_data_ready_semaphore

    阅读线程:

    1
    2
    3
    4
    5
    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)

    写线程

    1
    2
    3
    4
    sem_wait(vido_data_ready_semaphore)
    bufer_to_write = buf_dequeue(ready_buffer_queue)
    write_buffer
    buf_enqueue(bufer_to_write, empty_buffer_queue)

    如果您的编写线程在等待内核时被阻止,则可以这样做。
    但是,如果您被限制在内核空间之内,那么除了寻找比2.6.10

    更新的内核之外,您无能为力。


    在不了解您的特殊情况的情况下,我只能提供以下猜测:

    尝试使用fsync()/ sync()强制内核更频繁地将数据刷新到存储设备。听起来内核在执行实际写操作时会先缓冲所有写操作,然后束缚总线,或者使系统停顿。通过仔细调用fsync(),您可以尝试以更精细的方式安排在系统总线上的写入。

    以编码/捕获(您没有提到视频捕获,因此我在这里做个假设-您可能想要添加更多信息)任务的方式构建应用程序可能很有意义在自己的线程中,并在用户区中缓冲其输出-然后,第二个线程可以处理对设备的写入。这将为您提供一个平滑缓冲区,以允许编码器始终完成其写操作而不会阻塞。

    听起来可疑的一件事是,您仅以一定的数据速率看到此问题-如果这确实是一个缓冲问题,我希望该问题在较低的数据速率下发生的可能性降低,但是我希望仍然希望看到此问题。

    在任何情况下,更多信息可能会很有用。您的系统架构是什么? (非常笼统地说。)

    鉴于您提供的其他信息,对于小的写入次数和频繁的刷新操作,设备的吞吐量似乎很差。如果您确定对于较大的写入操作,您可以获得足够的吞吐量(我不确定是这种情况,但是文件系统可能做的很蠢,例如每次写入后更新FAT),然后编码线程将数据传送到写线程,并在写线程中有足够的缓冲以避免停顿。过去,我曾使用共享内存环形缓冲区来实现这种方案,但是任何允许编写器在不停顿除非缓冲区已满的情况下写入I / O进程而不会停顿的IPC机制都可以解决问题。 >


    您自己做flush()听起来很不错-您想控制住自己,而不是让它留在通用缓冲层的可变部分上。

    这可能很明显,但是请确保您不要经常调用write()-确保每个write()都有足够的数据要写入,以使syscall开销值得。另外,从另一个方向讲,不要太称呼它,否则它会阻塞足够长时间而导致问题。

    在更难以实现的Rails上,您是否尝试过切换到异步I / O?使用aio,您可以在将视频数据吸入另一组缓冲区时触发写操作并将其交给一组缓冲区,而在写操作完成后,您可以切换缓冲区集。


    首先很明显,您是否尝试过明确地告诉文件刷新?我还认为可能会有一些ioctl可以用来执行此操作,但是老实说,我并没有做太多的C / POSIX文件编程。

    看到您使用的是Linux内核,您应该能够对内核进行调整和重建,使其更适合您的需求,例如。

    会更频繁地刷新到永久性存储。

    快速查看我的手册页会发现以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    SYNC(2)                    Linux Programmer’s Manual                   SYNC(2)

    NAME
           sync - commit buffer cache to disk

    SYNOPSIS
           #include <unistd.h>

           void sync(void);

       Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

           sync(): _BSD_SOURCE || _XOPEN_SOURCE >= 500

    DESCRIPTION
           sync() first commits inodes to buffers, and then buffers to disk.

    ERRORS
           This function is always successful.

    展开全文阅读

    相关内容