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

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

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

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

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

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

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

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

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

Обновление:

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

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

Ответы

Ответ 1

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

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

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

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

Ответ 2

Я выскажу несколько советов, советы будут дешевыми.

  • убедитесь, что вы используете API нижнего уровня для записи на диск, не используйте функции кэширования пользовательского режима, такие как fopen, fread, fwrite, используя функции нижнего уровня open, read, write.
  • передайте флаг O_SYNC, когда вы откроете файл, это приведет к тому, что каждая операция записи будет заблокирована до тех пор, пока не будет записана на диск, что избавит вас от резкого поведения ваших записей... с учетом того, что каждая запись медленнее.
  • Если вы делаете чтение /ioctls с устройства, чтобы захватить фрагмент видеоданных, вам может потребоваться выделить область разделяемой памяти между приложением и ядром, в противном случае вас поразит куча вызовов copy_to_user при передаче буферов видеоданных из пространства ядра в пространство пользователя.
  • Возможно, вам потребуется проверить, что ваше USB-устройство флэш-памяти достаточно быстро, с постоянными переводами для записи данных.

Просто пара мыслей, надеюсь, что это поможет.

Ответ 4

Похоже, вы ищете файловые системы реального времени linux. Обязательно выполните поиск в Google и других.

XFS имеет опцию в реальном времени, хотя я не играл с ней.

hdparm может полностью отключить кэширование.

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

Но мое предложение состояло бы в том, чтобы избежать использования палки в качестве файловой системы вообще и вместо этого использовать ее как необработанное устройство. Вещи данные на нем, как вы бы использовали 'dd'. Затем в другом месте читайте эти необработанные данные и выпишите их после выпечки.

Конечно, я не знаю, подходит ли вам этот вариант.

Ответ 5

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

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

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

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

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

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

Ответ 6

Имеет ли отладочную помощь, вы можете использовать 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

Ответ 7

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

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

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

Ответ 8

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

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

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

То, что 0,6 секунды звучит подозрительно похоже на ваш "остановка", возможно, на полсекунды.

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

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

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

Ответ 9

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

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


Быстрая проверка на моих страницах man находит это:

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.

Ответ 10

Выполняя собственные звуки флеша(), я хочу контролировать, а не оставлять его в кавычках общего буферного слоя.

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

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