13

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

我发现在超过一定的数据速率(大约 750kbyte/sec)时,我开始看到用户级视频编写应用程序停止大约半秒,大约每 5 秒。这足以导致内核驱动程序耗尽缓冲区——即使我可以增加缓冲区的数量,视频数据也必须与其他实时发生的事情同步(最好在 40 毫秒内)。在这 5 秒的“滞后峰值”之间,写入在 40 毫秒内完成(就应用程序而言 - 我很欣赏它们被操作系统缓冲)

我认为这个延迟峰值与 Linux 将数据刷新到磁盘的方式有关——我注意到 pdflush 被设计为每 5 秒唤醒一次,我的理解是这就是写作的目的。一旦停顿结束,用户态应用程序就能够快速服务并写入缓冲区的积压(没有溢出)。

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

我正在寻找两种线索:

  1. 我怎样才能阻止突发性写入停止我的应用程序——可能是进程优先级、实时补丁或调整文件系统代码以连续写入而不是突发性写入?

  2. 如何让我的应用程序知道文件系统在写入积压和卡/棒的吞吐量方面发生了什么?我有能力动态更改硬件编解码器中的视频比特率,这比丢帧或对最大允许比特率施加人为上限要好得多。

更多信息:这是一个 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问题?:)

4

10 回答 10

10

根据记录,除了最极端的情况外,似乎有两个主要方面已经消除了这个问题。该系统仍在开发中,尚未经过彻底的酷刑测试,但运行良好(触摸木头)。

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

另一方面是关注物理介质的最终写入吞吐量。为此,我密切关注统计数据 Dirty: 由 /proc/meminfo 报告。如果 Dirty: 爬升到某个阈值以上,我有一些粗略且现成的代码来限制编码器,似乎工作模糊。以后需要更多的测试和调整。幸运的是,我有很多 RAM (128M) 可以使用,让我有几秒钟的时间来查看我的积压工作并顺利减少。

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

于 2008-10-18T20:47:20.753 回答
5

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

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

只是一些想法,希望这会有所帮助。

于 2008-09-16T23:18:06.057 回答
3

以下是一些关于调整 pdflush 以进行大量写入操作的信息。

于 2008-09-17T13:51:29.123 回答
2

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

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

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

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

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

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

于 2008-09-16T23:17:40.333 回答
1

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

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

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

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

无论如何,更多信息可能会被证明是有用的。你的系统架构是什么?(用非常笼统的术语来说。)

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

于 2008-09-16T23:10:32.393 回答
1

有调试帮助,你可以使用 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)

如果您的写入线程在等待内核时被阻塞,这可能会起作用。但是,如果您在内核空间中被阻塞,那么您无能为力,除了寻找比您的 2.6.10 更新的内核

于 2008-09-17T15:00:22.143 回答
1

一个有用的 Linux 函数和同步或 fsync 的替代方法是 sync_file_range。这使您可以安排数据写入,而无需等待内核缓冲区系统处理它。

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

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

于 2008-09-18T14:30:12.987 回答
1

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

但是,规范允许这些卡在完成操作之前以“忙”响应,并且显然卡可以声称忙的时间没有限制(请告诉我是否有这样的限制)。

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

这 0.6 秒听起来与您的“停止可能半秒”非常相似。

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

我听说过每次擦除闪存扇区然后重新编程时,都比上次花费的时间要长一些。

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

于 2010-11-18T04:04:22.987 回答
0

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

看到您使用的是 Linux 内核,您应该能够调整和重建内核以更好地满足您的需求,例如。更频繁,但对永久存储的刷新也更小。


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

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

姓名
       同步 - 将缓冲区缓存提交到磁盘

概要
       #include <unistd.h>

       无效同步(无效);

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

       同步():_BSD_SOURCE || _XOPEN_SOURCE >= 500

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

错误
       这个函数总是成功的。
于 2008-09-16T23:03:57.947 回答
0

做你自己的 flush()ing 听起来对我来说是正确的 - 你想要控制,而不是让它留给通用缓冲层的变幻莫测。

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

在更难以重新实现的轨道上,您是否尝试过切换到异步 i/o?使用 aio,您可以启动写入并将一组缓冲区交给它,同时将视频数据吸入另一组,当写入完成时,您切换缓冲区组。

于 2008-09-17T16:59:57.800 回答