Pregunta

Estoy trabajando en un proyecto de Linux integrado que conecta un ARM9 con un chip codificador de video de hardware y escribe el video en una tarjeta SD o memoria USB.La arquitectura del software implica un controlador de kernel que lee datos en un grupo de buffers y una aplicación de usuario que escribe los datos en un archivo en el dispositivo extraíble montado.

Estoy descubriendo que por encima de una cierta velocidad de datos (alrededor de 750 kbytes/seg), empiezo a ver que la aplicación de escritura de videos del usuario se detiene durante tal vez medio segundo, aproximadamente cada 5 segundos.Esto es suficiente para hacer que el controlador del kernel se quede sin buffers, e incluso si pudiera aumentar el número de buffers, los datos de video deben sincronizarse (idealmente dentro de 40 ms) con otras cosas que suceden en tiempo real.Entre estos "picos de retraso" de 5 segundos, las escrituras se completan en 40 ms (en lo que respecta a la aplicación, aprecio que el sistema operativo las almacene en el buffer)

Creo que este pico de retraso tiene que ver con la forma en que Linux vacía los datos en el disco. Observo que pdflush está diseñado para activarse cada 5 segundos, según tengo entendido, esto sería lo que escribiría.Tan pronto como se supera el bloqueo, la aplicación del área de usuario puede dar servicio y escribir rápidamente la acumulación de búferes (que no se desbordaron).

Creo que el dispositivo en el que estoy escribiendo tiene un rendimiento final razonable:copiar un archivo de 15 MB desde una memoria fs y esperar a que se complete la sincronización (y que la luz de la memoria USB deje de parpadear) me dio una velocidad de escritura de alrededor de 2,7 MBytes/seg.

Estoy buscando dos tipos de pistas:

  1. ¿Cómo puedo evitar que la escritura en ráfagas detenga mi aplicación, tal vez prioridades de proceso, parches en tiempo real o ajustar el código del sistema de archivos para escribir de forma continua en lugar de en ráfagas?

  2. ¿Cómo puedo hacer que mis aplicaciones estén al tanto de lo que sucede con el sistema de archivos en términos de trabajos pendientes de escritura y rendimiento en la tarjeta/dispositivo?Tengo la capacidad de cambiar la tasa de bits de video en el códec de hardware sobre la marcha, lo que sería mucho mejor que eliminar fotogramas o imponer un límite artificial a la tasa de bits máxima permitida.

Algo más de información:Este es un ARM9 de 200 MHz que actualmente ejecuta un kernel basado en Montavista 2.6.10.

Actualizaciones:

  • Montar el sistema de archivos SYNC hace que el rendimiento sea demasiado pobre.
  • El medio extraíble tiene formato FAT/FAT32 y debe tenerlo, ya que el propósito del diseño es que el medio se pueda conectar a cualquier PC con Windows y leer.
  • Llamar regularmente a sync() o fsync(), digamos, cada segundo provoca paradas regulares y un rendimiento inaceptablemente bajo
  • Estoy usando write() y open(O_WRONLY | O_CREAT | O_TRUNC) en lugar de fopen(), etc.
  • No puedo encontrar inmediatamente nada en línea sobre los "sistemas de archivos Linux en tiempo real" mencionados.¿Enlaces?

Espero que esto tenga sentido.¿Primera pregunta de Linux integrado sobre stackoverflow?:)

¿Fue útil?

Solución

Para que conste, resultó que hubo dos aspectos principales que parecen haber eliminado el problema en todos los casos, excepto en los más extremos.Este sistema todavía está en desarrollo y aún no ha sido probado exhaustivamente, pero funciona bastante bien (toco madera).

La gran victoria provino de hacer que la aplicación de escritura del espacio de usuario sea multiproceso.Son las llamadas a write() las que a veces bloquean:otros procesos y subprocesos aún se ejecutan.Siempre que tenga un hilo que atienda el controlador del dispositivo y actualice el recuento de fotogramas y otros datos para sincronizarlos con otras aplicaciones que se están ejecutando, los datos se pueden almacenar en el búfer y escribir unos segundos más tarde sin incumplir ningún plazo.Primero probé un simple buffer doble de ping-pong pero no fue suficiente;los buffers pequeños se verían abrumados y los grandes simplemente provocarían pausas más grandes mientras el sistema de archivos digería las escrituras.Un grupo de 10 buffers de 1 MB en cola entre subprocesos está funcionando bien ahora.

El otro aspecto es vigilar el rendimiento de escritura final en medios físicos.Para ello estoy atento a la estadística Sucio:informado por /proc/meminfo.Tengo un código aproximado y listo para acelerar el codificador si está sucio:sube por encima de un cierto umbral, parece funcionar vagamente.Se necesitarán más pruebas y ajustes más adelante.Afortunadamente, tengo mucha RAM (128 M) para jugar, lo que me da unos segundos para ver cómo se acumula mi trabajo pendiente y desacelerar sin problemas.

Intentaré recordar regresar y actualizar esta respuesta si encuentro que necesito hacer algo más para solucionar este problema.Gracias a los demás respondedores.

Otros consejos

Te daré algunas sugerencias, los consejos son baratos.

  • asegúrese de estar utilizando una API de nivel inferior para escribir en el disco, no utilice funciones de almacenamiento en caché en modo de usuario como fopen, fread, fwrite utilizar las funciones de nivel inferior open, read, write.
  • pasa el O_SYNC flag cuando abra el archivo, esto hará que cada operación de escritura se bloquee hasta que se escriba en el disco, lo que eliminará el comportamiento de ráfagas de sus escrituras... con el costo de que cada escritura sea más lenta.
  • Si está realizando lecturas/ioctls desde un dispositivo para capturar una porción de datos de video, es posible que desee considerar asignar una región de memoria compartida entre la aplicación y el kernel; de lo contrario, recibirá un montón de datos de video. copy_to_user llamadas al transferir buffers de datos de video desde el espacio del kernel al espacio del usuario.
  • Es posible que deba validar que su dispositivo flash USB sea lo suficientemente rápido con transferencias sostenidas para escribir los datos.

Sólo un par de pensamientos, espero que esto ayude.

Aquí Hay información sobre cómo ajustar pdflush para operaciones con mucha escritura.

Parece que estás buscando sistemas de archivos Linux en tiempo real.Asegúrese de buscar eso en Google et al.

XFS tiene una opción en tiempo real, aunque no he jugado con ella.

hdparm podría permitirle desactivar el almacenamiento en caché por completo.

Ajustar las opciones del sistema de archivos (desactivar todos los atributos de archivo adicionales innecesarios) puede reducir lo que necesita vaciar, acelerando así la descarga.Aunque dudo que eso ayude mucho.

Pero mi sugerencia sería evitar el uso del dispositivo como sistema de archivos y, en su lugar, usarlo como un dispositivo sin formato.Introduzca datos como lo haría con 'dd'.Luego, lea en otro lugar esos datos sin procesar y escríbalos después de hornearlos.

Por supuesto, no sé si esa es una opción para ti.

Tiene una ayuda de depuración, puedes usar strace para ver qué operaciones están tomando tiempo.Puede que haya algo sorprendente con FAT/FAT32.

¿Escribe en un solo archivo o en varios archivos?

Puede crear un hilo de lectura que mantendrá un grupo de búfer de video listo para escribir en una cola.Cuando se recibe una trama, se agrega a la cola y se señala el hilo de escritura.

Datos compartidos

empty_buffer_queue
ready_buffer_queue
video_data_ready_semaphore

Hilo de lectura:

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)

Hilo de escritura

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

Si su hilo de escritura está bloqueado esperando el kernel, esto podría funcionar.Sin embargo, si está bloqueado dentro del espacio del kernel, entonces no hay mucho que pueda hacer, excepto buscar un kernel más reciente que el 2.6.10.

Sin saber más sobre sus circunstancias particulares, sólo puedo ofrecer las siguientes conjeturas:

Intente usar fsync()/sync() para forzar al kernel a descargar datos al dispositivo de almacenamiento con más frecuencia.Parece que el kernel almacena en buffer todas sus escrituras y luego bloquea el bus o detiene su sistema mientras realiza la escritura real.Con llamadas cuidadosas a fsync() puede intentar programar escrituras en el bus del sistema de una manera más detallada.

Podría tener sentido estructurar la aplicación de tal manera que la tarea de codificación/captura (no mencionaste la captura de video, así que estoy haciendo una suposición aquí; es posible que quieras agregar más información) se ejecute en su propio hilo y almacena su salida en el área de usuario; luego, un segundo hilo puede manejar la escritura en el dispositivo.Esto le proporcionará un búfer de suavizado para permitir que el codificador siempre termine sus escrituras sin bloquearse.

Una cosa que suena sospechosa es que solo se ve este problema a una determinada velocidad de datos; si realmente se tratara de un problema de almacenamiento en búfer, esperaría que el problema ocurriera con menos frecuencia a velocidades de datos más bajas, pero aún así esperaría ver esto. asunto.

En cualquier caso, más información podría resultar útil.¿Cuál es la arquitectura de su sistema?(En términos muy generales).

Dada la información adicional que proporcionó, parece que el rendimiento del dispositivo es bastante pobre para escrituras pequeñas y descargas frecuentes.Si está seguro de que para escrituras más grandes puede obtener suficiente rendimiento (y no estoy seguro de que ese sea el caso, pero el sistema de archivos podría estar haciendo algo estúpido, como actualizar el FAT después de cada escritura), entonces tenga un hilo de codificación que canalice los datos. a un hilo de escritura con suficiente almacenamiento en búfer en el hilo de escritura para evitar paradas.He usado buffers circulares de memoria compartida en el pasado para implementar este tipo de esquema, pero cualquier mecanismo IPC que permita al escritor escribir en el proceso de E/S sin detenerse a menos que el buffer esté lleno debería funcionar.

Una función útil de Linux y una alternativa a sync o fsync es sync_file_range.Esto le permite programar la escritura de datos sin esperar a que el sistema de búfer del kernel lo haga.

Para evitar pausas largas, asegúrese de que su cola de IO (por ejemplo:/sys/block/hda/queue/nr_requests) es lo suficientemente grande.Esa cola es donde los datos pasan entre ser eliminados de la memoria y llegar al disco.

Tenga en cuenta que sync_file_range no es portátil y solo está disponible en los kernels 2.6.17 y posteriores.

Me han dicho que después de que el host envía un comando, las tarjetas MMC y SD "deben responder dentro de 0 a 8 bytes".

Sin embargo, la especificación permite que estas tarjetas respondan con "ocupado" hasta que hayan finalizado la operación, y aparentemente no hay límite de cuánto tiempo una tarjeta puede decir que está ocupada (por favor, dígame si existe tal límite).

Veo que algunos chips flash de bajo costo, como el M25P80, tienen un "tiempo máximo de borrado de un solo sector" garantizado de 3 segundos, aunque normalmente "sólo" requiere 0,6 segundos.

Esos 0,6 segundos suenan sospechosamente similar a su "estancamiento durante tal vez medio segundo".

Sospecho que la compensación entre chips de flash lentos y baratos y chips de flash rápidos y caros tiene algo que ver con la amplia variación en los resultados de las unidades flash USB:

He escuchado rumores de que cada vez que se borra un sector flash y luego se reprograma, lleva un poco más de tiempo que la última vez.

Entonces, si tiene una aplicación en la que el tiempo es crítico, es posible que deba (a) probar sus tarjetas SD y memorias USB para asegurarse de que cumplan con la latencia mínima, el ancho de banda, etc.requerido por su aplicación, y (b) volver a probar periódicamente o reemplazar preventivamente estos dispositivos de memoria.

Bueno, primero es obvio: ¿has intentado decirle explícitamente al archivo que se vacíe?También creo que podría haber algún ioctl que puedas usar para hacerlo, pero honestamente no he hecho mucha programación de archivos C/POSIX.

Al ver que está en un kernel de Linux, debería poder ajustar y reconstruir el kernel para adaptarlo mejor a sus necesidades, por ejemplo.descargas mucho más frecuentes pero también más pequeñas al almacenamiento permanente.


Una revisión rápida en mis páginas de manual encuentra esto:

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.

Hacer su propio lavado() me parece correcto: desea tener el control, no dejarlo a los caprichos de la capa de búfer genérica.

Esto puede ser obvio, pero asegúrese de no llamar a write() con demasiada frecuencia; asegúrese de que cada write() tenga suficientes datos para escribir para que la sobrecarga de la llamada al sistema valga la pena.Además, en la otra dirección, no lo llames muy raramente o se bloqueará durante el tiempo suficiente como para causar un problema.

En una pista más difícil de reimplementar, ¿ha intentado cambiar a E/S asíncrona?Al usar aio, puede realizar una escritura y entregarle un conjunto de búferes mientras absorbe datos de video en el otro conjunto, y cuando finaliza la escritura, cambia los conjuntos de búferes.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top