Bursty записывает на SD/USB, останавливая мои критичные по времени приложения во встроенном Linux

StackOverflow https://stackoverflow.com/questions/78157

Вопрос

Я работаю над проектом встроенного Linux, который соединяет ARM9 с чипом аппаратного видеокодера и записывает видео на SD-карту или USB-накопитель.Архитектура программного обеспечения включает в себя драйвер ядра, который считывает данные в пул буферов, и пользовательское приложение, которое записывает данные в файл на смонтированном съемном устройстве.

Я обнаружил, что при достижении определенной скорости передачи данных (около 750 кбайт/сек) я начинаю видеть, как пользовательское приложение для записи видео зависает примерно на полсекунды, примерно каждые 5 секунд.Этого достаточно, чтобы драйвер ядра исчерпал буферы — и даже если бы я мог увеличить количество буферов, видеоданные должны быть синхронизированы (в идеале в течение 40 мс) с другими вещами, которые происходят в реальном времени.Между этими 5-секундными «пиками задержки» запись завершается в течение 40 мс (что касается приложения — я ценю, что они буферизуются ОС)

Я думаю, что этот скачок задержки связан с тем, как Linux сбрасывает данные на диск - я отмечаю, что pdflush предназначен для пробуждения каждые 5 секунд, насколько я понимаю, именно это и будет записывать.Как только застой закончится, пользовательское приложение сможет быстро обслужить и записать накопившиеся буферы (которые не переполнились).

Я думаю, что устройство, на которое я пишу, имеет разумную максимальную пропускную способность:копирование файла размером 15 МБ из файловой системы памяти и ожидание завершения синхронизации (и когда индикатор USB-накопителя перестанет мигать) дало мне скорость записи около 2,7 МБ/сек.

Я ищу два вида подсказок:

  1. Как я могу предотвратить остановку моего приложения из-за пакетной записи - возможно, приоритеты обработки, исправления в реальном времени или настройка кода файловой системы для непрерывной, а не пакетной записи?

  2. Как я могу сообщить своим приложениям о том, что происходит с файловой системой с точки зрения очереди записи и пропускной способности карты/флешки?У меня есть возможность изменять битрейт видео в аппаратном кодеке на лету, что было бы намного лучше, чем пропускать кадры или налагать искусственное ограничение на максимально допустимый битрейт.

Еще немного информации:это ARM9 с тактовой частотой 200 МГц, в настоящее время работающий под управлением ядра Montavista 2.6.10.

Обновления:

  • Монтирование файловой системы SYNC приводит к слишком низкой пропускной способности.
  • Съемный носитель имеет формат FAT/FAT32 и должен быть таким, поскольку цель конструкции состоит в том, чтобы носитель можно было подключить к любому ПК с ОС Windows и прочитать.
  • Регулярный вызов sync() или fsync(), скажем, каждую секунду приводит к регулярным зависаниям и неприемлемо низкой пропускной способности.
  • Я использую write() и open(O_WRONLY | O_CREAT | O_TRUNC), а не fopen() и т. д.
  • Я не могу сразу найти в Интернете что-либо об упомянутых «файловых системах реального времени Linux».Ссылки?

Я надеюсь в этом есть смысл.Первый вопрос о встроенном Linux по stackoverflow?:)

Это было полезно?

Решение

Для справки: оказалось, что есть два основных аспекта, которые, похоже, устраняют проблему во всех случаях, кроме самых крайних.Эта система все еще находится в разработке и еще не была тщательно проверена на пытках, но работает довольно хорошо (пощупать дерево).

Большой победой стало то, что приложение для записи пользовательского пространства стало многопоточным.Иногда блокируются вызовы write():другие процессы и потоки все еще работают.Пока у меня есть поток, обслуживающий драйвер устройства и обновляющий счетчик кадров и другие данные для синхронизации с другими запущенными приложениями, данные можно буферизовать и записывать через несколько секунд, не нарушая при этом никаких сроков.Сначала я попробовал простой двойной буфер для пинг-понга, но этого было недостаточно;маленькие буферы будут перегружены, а большие просто вызовут большие паузы, пока файловая система обрабатывает записи.Пул из 10 буферов по 1 МБ, находящихся в очереди между потоками, теперь работает хорошо.

Другой аспект — следить за максимальной пропускной способностью записи на физические носители.Для этого я слежу за статистикой Dirty:сообщил /proc/meminfo.У меня есть приблизительный и готовый код для регулирования энкодера, если он грязный:поднимается выше определенного порога, кажется, смутно работает.Дополнительные испытания и настройки потребуются позже.К счастью, у меня много оперативной памяти (128 МБ), с которой я могу поиграть, что дает мне несколько секунд, чтобы увидеть, как мое отставание увеличивается и плавно снижается.

Я постараюсь не забыть вернуться и обновить этот ответ, если обнаружу, что мне нужно сделать что-то еще, чтобы решить эту проблему.Спасибо другим ответившим.

Другие советы

Подкину несколько предложений, советы недорогие.

  • убедитесь, что вы используете API более низкого уровня для записи на диск, не используйте функции кэширования пользовательского режима, такие как fopen, fread, fwrite использовать функции нижнего уровня open, read, write.
  • пройти O_SYNC при открытии файла, это приведет к блокировке каждой операции записи до тех пор, пока она не будет записана на диск, что устранит прерывистую работу вашей записи... при этом затраты на каждую запись будут медленнее.
  • Если вы выполняете операции чтения/ioctls с устройства для захвата фрагмента видеоданных, возможно, вам стоит рассмотреть возможность выделения области общей памяти между приложением и ядром, иначе вы столкнетесь с кучей видеоданных. 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)

Если ваша потоковая запись заблокирована в ожидании ядра, это может сработать.Однако, если вы заблокированы внутри пространства ядра, вы ничего не сможете сделать, кроме как поискать более свежее ядро, чем ваше 2.6.10.

Не зная больше о ваших конкретных обстоятельствах, я могу только предложить следующие предположения:

Попробуйте использовать fsync()/sync(), чтобы заставить ядро ​​чаще сбрасывать данные на устройство хранения.Похоже, что ядро ​​буферизует все ваши записи, а затем связывает шину или иным образом останавливает вашу систему во время фактической записи.Осторожно вызывая fsync(), вы можете попытаться запланировать запись по системной шине более детально.

Возможно, имеет смысл структурировать приложение таким образом, чтобы задача кодирования/захвата (вы не упомянули захват видео, поэтому я делаю предположение – возможно, вы захотите добавить дополнительную информацию) выполнялась в отдельном потоке и буферизует свой вывод в пользовательской области - тогда второй поток может обрабатывать запись на устройство.Это даст вам буфер сглаживания, который позволит кодировщику всегда завершать запись без блокировки.

Одна вещь, которая звучит подозрительно, заключается в том, что вы видите эту проблему только при определенной скорости передачи данных - если бы это действительно была проблема с буферизацией, я бы ожидал, что проблема будет возникать реже при более низких скоростях передачи данных, но я все равно ожидал бы увидеть это проблема.

В любом случае дополнительная информация может оказаться полезной.Какова архитектура вашей системы?(В очень общих чертах.)

Учитывая предоставленную вами дополнительную информацию, похоже, что пропускная способность устройства довольно низкая для небольших операций записи и частых сбросов.Если вы уверены, что для более крупных операций записи вы можете получить достаточную пропускную способность (и я не уверен, что это так, но файловая система может делать что-то глупое, например, обновлять FAT после каждой записи), тогда у вас есть поток данных конвейера кодирования. в поток записи с достаточной буферизацией в потоке записи, чтобы избежать зависаний.Раньше я использовал кольцевые буферы с общей памятью для реализации такой схемы, но любой механизм IPC, который позволял бы устройству записи писать в процесс ввода-вывода без остановки, пока буфер не полон, должен помочь.

Полезная функция Linux и альтернатива sync или fsync — sync_file_range.Это позволяет планировать запись данных, не дожидаясь, пока буферная система ядра обработает их.

Чтобы избежать долгих пауз, убедитесь, что ваша очередь ввода-вывода (например:/sys/block/hda/queue/nr_requests) достаточно велик.В этой очереди данные проходят между сбросом из памяти и поступлением на диск.

Обратите внимание, что sync_file_range не является переносимым и доступен только в ядрах 2.6.17 и более поздних версиях.

Мне сказали, что после того, как хост отправит команду, карты MMC и SD «должны ответить в пределах от 0 до 8 байт».

Однако спецификация позволяет этим картам отвечать «занято» до тех пор, пока они не завершат операцию, и, судя по всему, нет ограничений на то, как долго карта может утверждать, что она занята (пожалуйста, скажите мне, существует ли такой предел).

Я вижу, что некоторые недорогие флэш-чипы, такие как M25P80, имеют гарантированное «максимальное время стирания одного сектора» 3 секунды, хотя обычно для этого «всего» требуется 0,6 секунды.

Эти 0,6 секунды подозрительно похожи на ваше «остановку на полсекунды».

Я подозреваю, что компромисс между дешевыми, медленными флэш-чипами и дорогими, быстрыми флэш-чипами как-то связан с широкими различиями в результатах USB-накопителей:

Я слышал слухи, что каждый раз, когда сектор флэш-памяти стирается, а затем перепрограммируется, это занимает немного больше времени, чем в прошлый раз.

Поэтому, если у вас есть приложение, критичное ко времени, вам может потребоваться (а) протестировать ваши SD-карты и USB-накопители, чтобы убедиться, что они соответствуют минимальной задержке, пропускной способности и т. д.требуемые вашим приложением, и (б) периодически повторно тестируйте или упреждающе заменяйте эти устройства памяти.

Ну, во-первых, очевидно, пробовали ли вы явно указать файлу на сброс?Я также думаю, что для этого можно использовать какой-нибудь ioctl, но, честно говоря, я мало занимался программированием файлов C/POSIX.

Увидев, что вы используете ядро ​​Linux, вы сможете настроить и пересобрать ядро ​​так, чтобы оно лучше соответствовало вашим потребностям, например.гораздо более частые, но и меньшие сбросы в постоянное хранилище.


Быстрая проверка на моих страницах руководства показывает следующее:

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.

Мне кажется, что использование собственной функцииlush() звучит правильно: вы хотите контролировать ситуацию, а не оставлять ее на волю капризов общего буферного уровня.

Это может быть очевидно, но убедитесь, что вы не вызываете функцию write() слишком часто — убедитесь, что каждая функция write() имеет достаточно данных для записи, чтобы окупить затраты на системный вызов.Кроме того, в другом направлении не вызывайте его слишком редко, иначе он будет блокироваться достаточно долго, чтобы вызвать проблемы.

Пробовали ли вы переключиться на асинхронный ввод-вывод в случае более сложного для повторной реализации варианта?Используя aio, вы можете запустить запись и передать ей один набор буферов, в то время как вы всасываете видеоданные в другой набор, а когда запись заканчивается, вы переключаете наборы буферов.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top