
Hola habrozhiteli! Ya escribimos sobre el libro de Michael Kerrisk
"API de Linux". Guía completa " . Ahora hemos decidido publicar un extracto del libro "Administración del almacenamiento intermedio de E / S de archivos administrado en el núcleo"
Se puede forzar el restablecimiento de la memoria intermedia del núcleo para los archivos de salida. A veces, esto es necesario si la aplicación, antes de continuar trabajando (por ejemplo, un proceso que registra cambios en la base de datos), debe garantizar que la salida se escriba realmente en el disco (o al menos en la caché del disco de hardware).
Antes de considerar las llamadas al sistema utilizadas para controlar el almacenamiento en búfer del kernel, valdría la pena considerar varias definiciones relacionadas de SUSv3.
E / S sincronizada con integridad de datos e archivosEn SUSv3, el concepto de finalización de E / S sincronizada significa "una operación de E / S que condujo a una transferencia exitosa de datos [al disco] o fue diagnosticada como no exitosa".
SUSv3 define dos tipos diferentes de terminaciones de E / S sincronizadas. La diferencia entre los tipos se relaciona con los metadatos ("datos sobre datos") que describen el archivo. El kernel los almacena junto con los datos del archivo mismo. Los detalles de los metadatos del archivo se analizarán en la sección 14.4 al examinar los inodos de archivo. Mientras tanto, será suficiente tener en cuenta que los metadatos del archivo incluyen información como la información sobre el propietario del archivo y su grupo, los derechos de acceso al archivo, el tamaño del archivo, la cantidad de enlaces duros al archivo, las marcas de tiempo que muestran la hora de la última vez que se accedió al archivo, la hora en que se modificó por última vez y la hora del último cambio de metadatos, así como los punteros a los bloques de datos.
El primer tipo de finalización de E / S sincronizada en SUSv3 es la finalización de integridad de datos. Al actualizar los datos del archivo, se debe garantizar la transferencia de información suficiente para permitir una mayor extracción de estos datos para continuar trabajando.
- para una operación de lectura, esto significa que los datos del archivo solicitado se han transferido (desde el disco) al proceso. Si hay operaciones de escritura pendientes que pueden afectar los datos solicitados, los datos se transferirán al disco antes de leerlos.
- para una operación de escritura, esto significa que los datos especificados en la solicitud de escritura se transfirieron (al disco), como todos los metadatos de archivo necesarios para extraer estos datos. El punto clave a tener en cuenta: para garantizar que los datos se extraen del archivo modificado, no es necesario transferir todos los archivos medaten. Un ejemplo del atributo de metadatos de un archivo modificado que debe migrarse es su tamaño (si la operación de escritura aumenta el tamaño del archivo). Por el contrario, las marcas de tiempo del archivo a modificar no necesitarán transferirse al disco antes de que ocurra la recuperación de datos posterior.
El segundo tipo de finalización de E / S sincronizada definida en SUSv3 es la finalización de integridad de archivo. Esta es una opción avanzada para completar E / S sincronizadas con integridad de datos. La diferencia entre este modo es que durante la actualización del archivo, todos sus metadatos se transfieren al disco, incluso si esto no es necesario para la extracción posterior de los datos del archivo.
El sistema llama para controlar el almacenamiento intermedio del kernel durante la E / S de archivoLa llamada al sistema fsync () restablece todos los datos almacenados en el búfer y todos los metadatos que están asociados con un archivo abierto que tiene un descriptor fd. Llamar a fsync () coloca el archivo en un estado de integridad (archivo) después de completar la E / S sincrónica.
La llamada fsync () devuelve el control solo después de que se completa la transferencia de datos al dispositivo de disco (o al menos a su caché).
#include <unistd.h> int fsync(int fd);
Devuelve en caso de éxito 0 o -1 en caso de error
La llamada al sistema fdatasync () funciona exactamente como fsync (), pero coloca el archivo en un estado de integridad (datos) después de completar la E / S sincrónica.
#include <unistd.h> int fdatasync(int fd);
Devuelve en caso de éxito 0 o -1 en caso de error
El uso de fdatasync () reduce potencialmente la cantidad de operaciones de disco de dos requeridas por la llamada del sistema fsync () a una. Por ejemplo, si los datos del archivo han cambiado, pero el tamaño sigue siendo el mismo, llamar a fdatasync () solo obliga a actualizar los datos. (Ya se ha señalado anteriormente que para completar una operación de E / S síncrona con integridad de datos, no hay necesidad de transferir cambios a atributos como la última vez que se modificó el archivo). Por el contrario, llamar a fsync () también forzará la transferencia de metadatos al disco.
Tal reducción en el número de operaciones de E / S de disco será útil para aplicaciones individuales para las cuales el rendimiento y la actualización precisa de metadatos específicos (por ejemplo, marcas de tiempo) juegan un papel decisivo. Esto puede conducir a mejoras de rendimiento significativas para aplicaciones que producen múltiples actualizaciones de archivos a la vez. Debido a que los datos y metadatos del archivo generalmente se encuentran en diferentes partes del disco, la actualización de ambos requerirá búsquedas repetidas hacia adelante y hacia atrás en el disco.
En Linux 2.2 y versiones anteriores, fdatasync () se implementa como una llamada a fsync (), por lo que no aumenta el rendimiento.
A partir de la versión del kernel 2.6.17, Linux proporciona una llamada de sistema no estándar sync_file_range (). Le permite controlar con mayor precisión el proceso de vaciado de datos de archivos al disco que fdatasync (). Al llamar, puede especificar el área que se colocará en el archivo y establecer marcas que establezcan las condiciones para bloquear esta llamada. Consulte la página del manual sync_file_range (2) para obtener más detalles.
La llamada al sistema sync () hace que todas las memorias intermedias del núcleo que contienen información actualizada del archivo (es decir, bloques de datos, bloques de puntero, metadatos, etc.) se vacíen al disco.
#include <unistd.h> void sync(void);
En la implementación de Linux, la función sync () devuelve el control solo después de que todos los datos se hayan transferido al dispositivo de disco (o al menos a su caché). Pero en SUSv3, se permite que sync () simplemente introduzca la transferencia de datos para la operación de E / S en el plan y devuelva el control hasta que se complete la transferencia.
Un subproceso de kernel ejecutado continuamente descarga los búferes de kernel modificados en el disco si no se sincronizaron explícitamente durante 30 segundos. Esto se hace para evitar que los búferes de datos no estén sincronizados con el archivo de disco correspondiente durante largos períodos de tiempo (y no exponerlos al riesgo de pérdida en caso de una falla del sistema). En Linux 2.6, esta tarea es realizada por el hilo del núcleo pdflush. (En Linux 2.4, fue ejecutado por el hilo del núcleo kupdated).
El período (en centésimas de segundo) después del cual el búfer modificado debe ser vaciado al disco por el código de flujo pdflush se define en el archivo / proc / sys / vm / dirty_expire_centisecs. Los archivos adicionales en el mismo directorio controlan otras características de la operación realizada por la secuencia pdflush.
Active el modo de sincronización para todos los registros: O_SYNCAl especificar el indicador O_SYNC al llamar a open (), todas las operaciones de salida posteriores se realizan en modo síncrono:
fd = open(pathname, O_WRONLY | O_SYNC);
Después de esta llamada a open (), cada operación de escritura () realizada en un archivo vacía automáticamente los datos y metadatos del archivo en el disco (es decir, las escrituras se realizan como operaciones de escritura sincronizadas con integridad de archivo).
En versiones anteriores del sistema BSD, el indicador O_FSYNC se usaba para proporcionar la funcionalidad incluida con el indicador O_SYNC. En glibc, el indicador O_FSYNC se define como sinónimo de O_SYNC.
Impacto en el rendimiento del indicador O_SYNCEl uso del indicador O_SYNC (o las llamadas frecuentes a fsync (), fdatasync () o sync ()) pueden afectar en gran medida el rendimiento. En la mesa La Figura 13.3 muestra el tiempo requerido para escribir 1 millón de bytes en un archivo que se acaba de crear (en el sistema de archivos ext2) para varios tamaños de búfer con el indicador O_SYNC establecido y sin marcar. Los resultados se obtuvieron (usando el programa filebuff / write_bytes.c provisto en el código fuente del libro) usando el kernel "vanilla" versión 2.6.30 y el sistema de archivos ext2 con un tamaño de bloque de 4096 bytes. Cada línea contiene el valor promedio obtenido después de 20 inicios para un tamaño de búfer dado.
Tabla 13.3. El efecto del indicador O_SYNC en una velocidad de escritura de 1 millón de bytes
Como puede ver, la especificación de la bandera O_SYNC conduce a un aumento monstruoso en el tiempo dedicado al usar un búfer de 1 byte más de 1000 veces. Tenga en cuenta también la gran diferencia que se produce al ejecutar registros con el indicador O_SYNC entre el tiempo transcurrido y el tiempo de uso de la CPU. Es una consecuencia del bloqueo de la ejecución del programa cuando el contenido real de cada búfer se vacía en el disco.
En los resultados que se muestran en la tabla. 13.3, otro factor que afecta el rendimiento cuando se usa O_SYNC no se tiene en cuenta. Las unidades de disco modernas tienen una memoria caché interna grande y, de forma predeterminada, la configuración del indicador O_SYNC simplemente transfiere datos a esta memoria caché. Si deshabilita el almacenamiento en caché de disco (usando el comando hdparm –W0), el impacto en el rendimiento de O_SYNC será aún más significativo. Con un tamaño de búfer de 1 byte, el tiempo transcurrido aumentará de 1030 segundos a aproximadamente 16,000 segundos. Con un tamaño de búfer de 4096 bytes, el tiempo transcurrido aumentará de 0,34 segundos a 4 segundos. Como resultado, si necesita forzar que las memorias intermedias del kernel se vacíen en el disco, debe considerar si es posible diseñar la aplicación utilizando memorias intermedias más grandes para write () o considere usar llamadas periódicas fsync () o fdatasync () en lugar del indicador O_SYNC.
Banderas O_DSYNC y O_RSYNCSUSv3 define dos indicadores de estado de archivo abierto adicionales relacionados con E / S sincronizada: O_DSYNC y O_RSYNC.
El indicador O_DSYNC da como resultado operaciones de escritura sincronizadas posteriores con integridad de datos de E / S terminadas (similar al uso de fdatasync ()). El efecto de su operación es diferente del efecto causado por el indicador O_SYNC, cuyo uso conduce a operaciones de escritura sincronizadas posteriores con integridad de archivo (como fsync ()).
El indicador O_RSYNC se especifica junto con O_SYNC o O_DSYNC y conduce a una extensión del comportamiento asociado con estos indicadores durante las operaciones de lectura. Especificar los indicadores O_RSYNC y O_DSYNC al abrir el archivo da como resultado operaciones de lectura sincronizadas posteriores con integridad de datos (es decir, antes de que se complete la lectura, todas las entradas de archivo pendientes se completan debido a la presencia de O_DSYNC). Especificar los indicadores O_RSYNC y O_SYNC al abrir el archivo conduce a operaciones de lectura sincronizadas posteriores con integridad del archivo (es decir, antes de que se complete la lectura, todas las entradas de archivo pendientes se completan debido a la presencia de O_SYNC).
Antes del lanzamiento de la versión 2.6.33 del núcleo, los indicadores O_DSYNC y O_RSYNC no se implementaron en Linux, y estas constantes se definieron en los archivos de encabezado glibc como la configuración del indicador O_SYNC. (En el caso de O_RSYNC, esto no era cierto, ya que O_SYNC no afecta ninguna característica funcional de las operaciones de lectura).
A partir de la versión del kernel 2.6.33, Linux implementa el indicador O_DSYNC, y es probable que la implementación del indicador O_RSYNC se agregue en futuras versiones del kernel.
Antes del lanzamiento del kernel 2.6.33 en Linux, no había una implementación completa de la semántica O_SYNC. En cambio, el indicador O_SYNC se implementó como O_DSYNC. En aplicaciones vinculadas con versiones anteriores de la biblioteca GNU C para núcleos anteriores, en las versiones de Linux 2.6.33 y posteriores, el indicador O_SYNC todavía se comporta como O_DSYNC. Esto se hace para mantener el comportamiento familiar de tales programas. (Para preservar la compatibilidad binaria hacia atrás en el kernel 2.6.33, al indicador O_DSYNC se le asignó el indicador O_SYNC anterior, y el nuevo indicador O_SYNC incluye el indicador O_DSYNC (04010000 y 010000 respectivamente en una de las máquinas). Esto permite que las aplicaciones se compilen con nuevos archivos de encabezado , obtenga al menos la semántica O_DSYNC en los núcleos lanzados antes de la versión 2.6.33.)
13.4 Descripción general del almacenamiento intermedio de E / S
En la fig. La Figura 13.1 muestra el esquema de almacenamiento en búfer utilizado (para archivos de salida) por la biblioteca stdio y el kernel, y también muestra los mecanismos para controlar cada tipo de almacenamiento en búfer. Si baja el gráfico hasta el centro, verá la transferencia de datos del usuario por las funciones de la biblioteca stdio al búfer stdio, que funciona en el espacio de memoria del usuario. Cuando este búfer está lleno, la biblioteca stdio recurre a la llamada al sistema write (), que transfiere datos al caché del búfer del núcleo (ubicado en la memoria del núcleo). Como resultado, el núcleo inicia una operación de disco para transferir datos al disco.
En la parte izquierda del circuito en la fig. 13.1 muestra las llamadas que se pueden usar en cualquier momento para forzar explícitamente el vaciado de cualquiera de las memorias intermedias. La parte derecha muestra las llamadas que se pueden usar para realizar un restablecimiento automático, ya sea desactivando el almacenamiento en búfer en la biblioteca stdio o activando la salida del archivo de un modo de ejecución síncrono para las llamadas del sistema, de modo que cada llamada de escritura () se transfiera inmediatamente al disco.
13.5 Notificación de E / S del núcleo
La llamada al sistema posix_fadvise () permite que el proceso informe al núcleo de su método preferido para acceder a los datos del archivo.
El kernel puede (pero no tiene que) usar la información proporcionada por la llamada al sistema posix_fadvise () para optimizar su uso de la memoria caché del búfer, aumentando así el rendimiento de E / S para el proceso y para el sistema en su conjunto. Llamar a posix_fadvise () no afecta la semántica del programa.
#define _XOPEN_SOURCE 600 #include <fcntl.h> int posix_fadvise(int fd, off_t offset, off_t len, int advice);
Devuelve el éxito 0 o un número de error positivo cuando ocurre
El argumento fd es un descriptor de archivo que identifica el archivo para el que se debe contactar al núcleo. Los argumentos offset y len identifican el área del archivo al que se refiere la notificación: offset indica el desplazamiento inicial del área, y len indica su tamaño en bytes. Establecer len en 0 significa que todos los bytes están destinados, comenzando con desplazamiento y terminando con el final del archivo. (En las versiones de kernel anteriores a 2.6.6, el valor 0 para len se interpretaba literalmente como 0 bytes).
El argumento de asesoramiento muestra la naturaleza prevista del acceso del proceso al archivo. Se define con uno de los siguientes valores.
POSIX_FADV_NORMAL: el proceso no tiene una notificación especial con respecto a los patrones de tratamiento. Este es el comportamiento predeterminado si no se dan notificaciones para el archivo. En Linux, esta operación establece la ventana para leer proactivamente los datos de un archivo a su tamaño original (128 KB).
POSIX_FADV_SEQUENTIAL: el proceso implica la lectura secuencial de datos de compensaciones más pequeñas a más grandes. En Linux, esta operación establece la ventana para leer proactivamente los datos de un archivo para duplicar su valor original.
POSIX_FADV_RANDOM: el proceso implica acceder a los datos en orden aleatorio. En Linux, esta opción deshabilita la lectura proactiva de datos de un archivo.
POSIX_FADV_WILLNEED: el proceso implica acceder al área especificada del archivo en un futuro próximo. El núcleo lee los datos de manera preventiva para llenar el caché del búfer con datos de archivos en el rango especificado por los argumentos offset y len. Las siguientes llamadas read () al archivo no bloquean las E / S del disco, sino que simplemente recuperan datos de la memoria caché del búfer. El núcleo no garantiza el tiempo que los datos recuperados del archivo están en la memoria caché del búfer. Si durante la operación de otro proceso o núcleo hay una necesidad especial de memoria, la página eventualmente se reutilizará. En otras palabras, si la memoria tiene una gran demanda, debemos garantizar un pequeño intervalo de tiempo entre la llamada a posix_fadvise () y la llamada posterior (o llamadas) para leer (). (La llamada al sistema readahead () específica de Linux proporciona una funcionalidad equivalente a la operación POSIX_FADV_WILLNEED).
POSIX_FADV_DONTNEED: el proceso no implica llamadas al área de archivo especificada en un futuro próximo. De esta manera, se notifica al núcleo que puede liberar las páginas de caché correspondientes (si las hay). En Linux, esta operación se realiza en dos etapas. Primero, si la cola de escritura en el dispositivo host no está llena de una serie de solicitudes, el núcleo descarta cualquier página de caché modificada en el área especificada. El núcleo luego intenta liberar todas las páginas de caché del área especificada. Para las páginas modificadas en esta área, la segunda etapa se completará con éxito solo si se grabaron en el dispositivo base durante la primera etapa, es decir, la cola de grabación en el dispositivo no está llena. Como la aplicación no puede verificar el estado de la cola en el dispositivo, puede garantizar que las páginas de caché se liberen llamando a fsync () o fdatasync () en el controlador fd antes de aplicar POSIX_FADV_DONTNEED.
POSIX_FADV_NOREUSE: el proceso implica un acceso único a los datos en el área especificada del archivo, sin reutilizarlo. Por lo tanto, se notifica al núcleo que puede liberar páginas después de un solo acceso a ellas. En Linux, esta operación se está ignorando actualmente.
La especificación posix_fadvise () apareció solo en SUSv3, y esta interfaz no es compatible con todas las implementaciones de UNIX. En Linux, la llamada posix_fadvise () se ha proporcionado desde la versión 2.6 del kernel.
13.6 Bypass de caché de búfer: E / S directa
A partir de la versión 2.4 del kernel, Linux permite que una aplicación omita la memoria caché del búfer al realizar E / S de disco al mover datos directamente desde el espacio de memoria del usuario a un archivo o dispositivo de disco. Algunas veces este modo se llama E / S directa o sin procesar.
La información proporcionada aquí es solo para Linux y no está estandarizada en SUSv3. Sin embargo, algunas opciones de acceso directo de E / S para dispositivos o archivos son proporcionadas por la mayoría de las implementaciones de UNIX.
A veces, la E / S directa no se entiende como un medio para lograr un alto rendimiento de E / S. Pero para la mayoría de las aplicaciones, el uso de E / S directa puede reducir significativamente el rendimiento. El hecho es que el kernel realiza varias optimizaciones para mejorar el rendimiento de E / S mediante el uso de una memoria caché de búfer, incluida la lectura secuencial proactiva de datos, la realización de E / S en grupos de bloques de disco y permitiendo que los procesos accedan al mismo volumen el mismo archivo, comparte buffers en el caché. Todos estos tipos de optimización cuando se usa E / S directa se pierden. Está destinado solo a aplicaciones con requisitos de E / S especializados, por ejemplo, sistemas de administración de bases de datos que realizan su propio almacenamiento en caché y optimización de E / S, y que no necesitan que el kernel pierda tiempo y memoria de la CPU para realizar las mismas tareas.
La entrada-salida directa se puede realizar en relación con un solo archivo o en relación con un dispositivo de bloque (por ejemplo, un disco). Para hacer esto, al abrir un archivo o dispositivo usando la llamada open (), se especifica el indicador O_DIRECT.
El indicador O_DIRECT funciona desde la versión del kernel 2.4.10. El uso de este indicador no es compatible con todos los sistemas de archivos y versiones del kernel de Linux. La mayoría de los sistemas de archivos básicos admiten el indicador O_DIRECT, pero muchos sistemas de archivos que no son UNIX (como VFAT) no. Puede probar el soporte para esta característica probando el sistema de archivos seleccionado (si el sistema de archivos no es compatible con O_DIRECT, al abrir open () fallará con un error EINVAL) o al examinar el código fuente del núcleo para esto.
Si un proceso abrió el archivo con el indicador O_DIRECT, y el otro de la manera habitual (es decir, utilizando el caché del búfer), entonces no hay coherencia entre el contenido del caché del búfer y los datos leídos o escritos a través de E / S directa. Tal desarrollo debe ser evitado.
Puede encontrar información sobre el método desactualizado (ahora no recomendado) para obtener acceso sin formato a un dispositivo de disco en la página del manual sin formato (8).
Restricciones de alineación para E / S directasDado que la E / S directa (tanto en dispositivos de disco como en relación con los archivos) implica el acceso directo al disco, se deben observar algunas limitaciones al realizar E / S.
- el búfer de datos portátil debe estar alineado en el borde de la memoria, un múltiplo del tamaño del bloque.
- El desplazamiento en el archivo o en el dispositivo desde el que comienzan los datos transferidos debe ser un múltiplo del tamaño del bloque.
- La longitud de los datos transferidos debe ser un múltiplo del tamaño del bloque.
El incumplimiento de cualquiera de estas restricciones dará como resultado un error EINVAL. En la lista anterior, el tamaño del bloque se refiere al tamaño del bloque físico del dispositivo (generalmente 512 bytes).
Al realizar E / S directa en Linux 2.4, se imponen más restricciones que en Linux 2.6: la alineación, la longitud y el desplazamiento deben ser múltiplos del tamaño del bloque lógico del sistema de archivos utilizado. (Normalmente, los tamaños de los bloques lógicos en un sistema de archivos son 1024, 2048 o 4096 bytes).
Programa ejemplo13.1 O_DIRECT . , ( ) , , , , , , , read(). 4096 .
, :
$ ./direct_read /test/x 512 512 0 Read 512 bytes $ ./direct_read /test/x 256 ERROR [EINVAL Invalid argument] read 512 $ ./direct_read /test/x 512 1 ERROR [EINVAL Invalid argument] read 512 $ ./direct_read /test/x 4096 8192 512 Read 4096 bytes $ ./direct_read /test/x 4096 512 256 ERROR [EINVAL Invalid argument] read 512
13.1 , , , memalign(). memalign() 7.1.4.
#define _GNU_SOURCE /* O_DIRECT <fcntl.h> */ #include <fcntl.h> #include <malloc.h> #include "tlpi_hdr.h" int main(int argc, char *argv[]) { int fd; ssize_t numRead; size_t length, alignment; off_t offset; void *buf; if (argc < 3 || strcmp(argv[1], "–help") == 0) usageErr("%s file length [offset [alignment]]\n", argv[0]); length = getLong(argv[2], GN_ANY_BASE, "length"); offset = (argc > 3) ? getLong(argv[3], GN_ANY_BASE, "offset") : 0; alignment = (argc > 4) ? getLong(argv[4], GN_ANY_BASE, "alignment") : 4096; fd = open(argv[1], O_RDONLY | O_DIRECT); if (fd == -1) errExit("open"); /* memalign() , , . 'buf' , 'alignment', . , , , , 256 , , 512- . '(char *)' ( 'void *', memalign(). */ buf = (char *) memalign(alignment * 2, length + alignment) + alignment; if (buf == NULL) errExit("memalign"); if (lseek(fd, offset, SEEK_SET) == -1) errExit("lseek"); numRead = read(fd, buf, length); if (numRead == -1) errExit("read"); printf("Read %ld bytes\n", (long) numRead); exit(EXIT_SUCCESS); } _______________________________________________________________filebuff/direct_read.c
»Se puede encontrar más información sobre el libro en el sitio web del editor»
Contenidos»
Extracto20% de descuento en cupones para Linux - Linux