2008-11-03 16 views
28

Resulta todo este malentendido de los tallos open() versus fopen() de un controlador I2C defectuoso en el núcleo de Linux 2.6.14 en un ARM. Backporting un controlador de bits de trabajo resuelto resolvió la causa raíz del problema que estaba tratando de abordar aquí.¿Cómo se puede vaciar una escritura usando un descriptor de archivo?

Estoy tratando de resolver un problema con un controlador de dispositivo serie en Linux (I2C). Parece que al agregar pausas del sistema operativo temporizado (duerme) entre escrituras y lecturas en el dispositivo las cosas funcionan ... (mucho) mejor.

Aparte: La naturaleza de I2C es que cada byte leído o escrito por el maestro es reconocido por el dispositivo en el otro extremo del cable (esclavo) - las pausas mejorar las cosas me animan a pensar en el controlador como trabajar de forma asíncrona, algo que no puedo conciliar con el funcionamiento del autobús. Anyhoo ...

que había ya sea como a ras la escritura para estar seguro (en lugar de utilizar la pausa de duración fija), o alguna prueba que la escritura/lectura de transacciones ha completado en una manera amistosa multi-threaded.

El problema con el uso de fflush(fd); es que requiere 'fd' para ser puntero de secuencia (no es un descriptor de archivo), es decir

FILE * fd = fopen("filename","r+"); 
... // do read and writes 
fflush(fd); 

Mi problema es que no requiere el uso de la ioctl(), que no lo hace usa un puntero de flujo es decir,

int fd = open("filename",O_RDWR); 
ioctl(fd,...); 

Sugerencias?

+1

En el código de usuario-tierra, la función de escritura() no se almacena automáticamente en el búfer. No necesita enjuagarlo; es autolavado. De hecho, a menos que use llamadas especiales al sistema, también es síncrono con el controlador del dispositivo. –

+0

Si está escribiendo un módulo de nivel kernel, las reglas pueden ser algo diferentes, pero probablemente debería estar utilizando el descriptor de archivo i/o no estándar (puntero de archivo) i/o. –

Respuesta

23

tiene dos opciones:

  1. Use fileno() para obtener el descriptor de fichero asociado con el puntero stdio corriente

  2. No utilice <stdio.h> en absoluto, de esa manera no es necesario Preocuparse por el enjuague tampoco: todas las escrituras irán al dispositivo inmediatamente, y para los dispositivos de caracteres, la llamada write() ni siquiera volverá hasta que el IO de nivel inferior se haya completado (en teoría).

Para a nivel de dispositivo IO yo diría que es bastante inusual para usar stdio. Me gustaría recomendar encarecidamente el uso de la de nivel inferior open(), read() y write() funciones en lugar (en base a su respuesta más adelante):

int fd = open("/dev/i2c", O_RDWR); 
ioctl(fd, IOCTL_COMMAND, args); 
write(fd, buf, length); 
+0

Evitar es lo que he estado haciendo; el "A un lado" en mi publicación original me llevó a pensar que los descriptores de archivos _might_ han sido almacenados temporalmente. Gracias por confirmar que no lo están y que debería buscar otra razón para golpear el bus I2C y causar problemas. – Jamie

+1

Vamos a aclarar la terminología: un descriptor de archivo UNIX es un índice entero y se supone que está sin búfer para que una llamada de escritura/lectura sea inmediata. Se supone que es muy eficiente para escrituras "grandes" e ineficiente para escrituras de un solo byte. fopen te da E/S en búfer. Use abrir/leer/escribir. – plinth

7

fflush() sólo se vacía el búfer añadido por la capa de stdio fopen(), como gestionado por el objeto FILE * . El archivo subyacente en sí, como lo ve el kernel, no está almacenado en este nivel. Esto significa que las escrituras que omiten la capa FILE *, usando fileno() y un write() crudo, tampoco se almacenan en el buffer de forma que fflush() se vaciarán.

Como han señalado otros, intente no mezclando los dos. Si necesita usar funciones de E/S "en bruto" como ioctl(), entonces open() el archivo usted mismo directamente, sin usando fopen<() y amigos de stdio.

1

Si quieres ir a la inversa (asociado ARCHIVO * con el descriptor de archivo existente), utilice fdopen():

              FDOPEN(P) 

NAME 

     fdopen - associate a stream with a file descriptor 

SYNOPSIS 

     #include <stdio.h> 

     FILE *fdopen(int fildes, const char *mode); 
2

suena como lo que busca es la función fsync() (o fdatasync()?), o podría usar el indicador O_SYNC en su llamada a open().

25

Creo que lo que busca puede ser

int fsync(int fd); 

o

int fdatasync(int fd); 

fsync eliminará el archivo de búfer del núcleo en el disco. fdatasync también lo hará a excepción de los metadatos.

+1

El OP indicó que este es un dispositivo de caracteres i2c, no un disco. – Alnitak

+1

De acuerdo, los nodos de desarrollo i2c no tienen búfer de página, por lo que no necesitan enjuagarse/sincronizarse. Los errores en el controlador del dispositivo determinan lo que realmente se hace. –

+2

Aunque esta no es la solución a la pregunta del OP, es útil para otros que preguntan la pregunta principal del título. Estoy ** escribiendo ** en un disco a través de un fd, así que fue muy útil ver cómo limpiar los almacenamientos intermedios del kernel. – Eponymous

1

¿Has probado a deshabilitar el almacenamiento en búfer?

setvbuf(fd, NULL, _IONBF, 0); 
+1

Esto funcionó para mí. No estoy seguro de cuál es mi configuración, pero usé esto y "fsync" (usando fileno para obtener el int en lugar de un FILE *) y pude buscar y leer desde el archivo después de cerrarlo y volver a abrirlo. – HappyPandaFace

Cuestiones relacionadas