La E/S de disco asincrónica aparece como sincrónica en Windows

Este artículo le ayuda a resolver el problema en el que el comportamiento predeterminado de E/S es sincrónico, pero aparece como asincrónico.

              Versión original del producto: Windows
Número de KB original: 156932

Resumen

La E/S de archivo en Microsoft Windows puede ser sincrónica o asincrónica. El comportamiento predeterminado de E/S es sincrónico, donde se llama a una función de E/S y se devuelve cuando se completa la E/S. La E/S asincrónica permite que una función de E/S devuelva la ejecución al autor de la llamada inmediatamente, pero no se supone que la E/S esté completa hasta algún momento futuro. El sistema operativo notifica al autor de la llamada cuando se completa la E/S. En su lugar, el autor de la llamada puede determinar el estado de la operación de E/S pendiente mediante los servicios del sistema operativo.

La ventaja de la E/S asincrónica es que el autor de la llamada tiene tiempo para realizar otro trabajo o emitir más solicitudes mientras se completa la operación de E/S. El término E/S superpuesta se usa con frecuencia para E/S asincrónica y E/S no superpuesta para E/S sincrónica. En este artículo se usan los términos Asincrónico y Sincrónico para las operaciones de E/S. En este artículo se supone que el lector está familiarizado con las funciones de E/S de archivo como CreateFile, ReadFile, WriteFile.

Con frecuencia, las operaciones de E/S asincrónicas se comportan como E/S sincrónica. Ciertas condiciones que se describen en este artículo en las secciones posteriores, lo que hace que las operaciones de E/S se completen sincrónicamente. El autor de la llamada no tiene tiempo para el trabajo en segundo plano porque las funciones de E/S no devuelven hasta que se completa la E/S.

Varias funciones están relacionadas con la E/S sincrónica y asincrónica. En este artículo se usan ReadFile y WriteFile como ejemplos. Las buenas alternativas serían ReadFileEx y WriteFileEx. Aunque en este artículo solo se describe específicamente la E/S de disco, muchos de los principios se pueden aplicar a otros tipos de E/S, como E/S serie o E/S de red.

Configuración de E/S asincrónica

La FILE_FLAG_OVERLAPPED marca debe especificarse en CreateFile cuando se abre el archivo. Esta marca permite que las operaciones de E/S en el archivo se realicen de forma asincrónica. A continuación le mostramos un ejemplo:

HANDLE hFile;

hFile = CreateFile(szFileName,
                      GENERIC_READ,
                      0,
                      NULL,
                      OPEN_EXISTING,
                      FILE_FLAG_NORMAL | FILE_FLAG_OVERLAPPED,
                      NULL);

if (hFile == INVALID_HANDLE_VALUE)
      ErrorOpeningFile();

Tenga cuidado al codificar para E/S asincrónica porque el sistema se reserva el derecho de realizar una operación sincrónica si es necesario. Por lo tanto, es mejor si escribe el programa para controlar correctamente una operación de E/S que se puede completar de forma sincrónica o asincrónica. El código de ejemplo muestra esta consideración.

Hay muchas cosas que un programa puede hacer mientras espera a que se completen las operaciones asincrónicas, como poner en cola operaciones adicionales o realizar un trabajo en segundo plano. Por ejemplo, el código siguiente controla correctamente la finalización superpuesta y no superpuesta de una operación de lectura. No hace nada más que esperar a que se complete la E/S pendiente:

if (!ReadFile(hFile,
               pDataBuf,
               dwSizeOfBuffer,
               &NumberOfBytesRead,
               &osReadOperation )
{
   if (GetLastError() != ERROR_IO_PENDING)
   {
      // Some other error occurred while reading the file.
      ErrorReadingFile();
      ExitProcess(0);
   }
   else
      // Operation has been queued and
      // will complete in the future.
      fOverlapped = TRUE;
}
else
   // Operation has completed immediately.
   fOverlapped = FALSE;

if (fOverlapped)
{
   // Wait for the operation to complete before continuing.
   // You could do some background work if you wanted to.
   if (GetOverlappedResult( hFile,
                           &osReadOperation,
                           &NumberOfBytesTransferred,
                           TRUE))
      ReadHasCompleted(NumberOfBytesTransferred);
   else
      // Operation has completed, but it failed.
      ErrorReadingFile();
}
else
   ReadHasCompleted(NumberOfBytesRead);

Nota:

&NumberOfBytesRead pasar en ReadFile es diferente de pasar &NumberOfBytesTransferred a GetOverlappedResult. Si se ha realizado una operación asincrónica, GetOverlappedResult se usa para determinar el número real de bytes transferidos en la operación una vez completada. El &NumberOfBytesRead objeto pasado a ReadFile no tiene sentido.

Por otro lado, si una operación se completa inmediatamente, se &NumberOfBytesRead pasa a ReadFile es válida para el número de bytes leídos. En este caso, omita la OVERLAPPED estructura pasada a ReadFile; no la use con GetOverlappedResult o WaitForSingleObject.

Otra advertencia con la operación asincrónica es que no debe usar una OVERLAPPED estructura hasta que se complete su operación pendiente. En otras palabras, si tiene tres operaciones de E/S pendientes, debe usar tres OVERLAPPED estructuras. Si reutiliza una OVERLAPPED estructura, recibirá resultados impredecibles en las operaciones de E/S y puede experimentar daños en los datos. Además, debe inicializarlo correctamente para que no haya datos restantes que afecten a la nueva operación antes de poder usar una OVERLAPPED estructura por primera vez o antes de reutilizarla después de que se haya completado una operación anterior.

El mismo tipo de restricción se aplica al búfer de datos usado en una operación. Un búfer de datos no debe leerse ni escribirse hasta que se haya completado su operación de E/S correspondiente; leer o escribir el búfer puede producir errores y datos dañados.

La E/S asincrónica todavía parece ser sincrónica

Sin embargo, si siguió las instrucciones anteriores en este artículo, todas las operaciones de E/S se siguen completando normalmente sincrónicamente en el orden emitido y ninguna de las ReadFile operaciones devuelve FALSE con GetLastError() la devolución ERROR_IO_PENDINGde , lo que significa que no tiene tiempo para ningún trabajo en segundo plano. ¿Por qué ocurre esto?

Hay varias razones por las que las operaciones de E/S se completan sincrónicamente, incluso si ha codificado para la operación asincrónica.

Compresión

Una obstrucción a la operación asincrónica es la compresión de New Technology File System (NTFS). El controlador del sistema de archivos no accederá a los archivos comprimidos de forma asincrónica; en su lugar, todas las operaciones se realizan sincrónicas. Esta obstrucción no se aplica a los archivos comprimidos con utilidades similares a COMPRESS o PKZIP.

Cifrado NTFS

De forma similar a la compresión, el cifrado de archivos hace que el controlador del sistema convierta la E/S asincrónica en sincrónica. Si los archivos se descifran, las solicitudes de E/S serán asincrónicas.

Extensión de un archivo

Otra razón por la que las operaciones de E/S se completan sincrónicamente son las propias operaciones. En Windows, cualquier operación de escritura en un archivo que extienda su longitud será sincrónica.

Nota:

Las aplicaciones pueden hacer que la operación de escritura mencionada anteriormente sea asincrónica cambiando la longitud de datos válida del archivo mediante la SetFileValidData función y, a continuación, emitiendo .WriteFile

Con SetFileValidData (que está disponible en Windows XP y versiones posteriores), las aplicaciones pueden ampliar archivos de forma eficaz sin incurrir en una penalización de rendimiento por rellenarlos sin problemas.

Dado que el sistema de archivos NTFS no rellena los datos de forma cero hasta la longitud de datos válida (VDL) definida por SetFileValidData, esta función tiene implicaciones de seguridad en las que el archivo puede tener clústeres asignados previamente ocupados por otros archivos. Por lo tanto, SetFileValidData requiere que el autor de la llamada tenga el nuevo SeManageVolumePrivilege habilitado (de forma predeterminada, esto solo se asigna a los administradores). Microsoft recomienda que los proveedores de software independientes (ISV) consideren cuidadosamente las implicaciones del uso de dicha función.

Caché

La mayoría de los controladores de E/S (disco, comunicaciones y otros) tienen código de caso especial donde, si una solicitud de E/S se puede completar inmediatamente, la operación se completará y la ReadFile función o WriteFile devolverá TRUE. En todos los sentidos, estos tipos de operaciones parecen ser sincrónicos. En el caso de un dispositivo de disco, normalmente, una solicitud de E/S se puede completar inmediatamente cuando los datos se almacenan en caché en la memoria.

Los datos no están en la memoria caché

Sin embargo, el esquema de caché puede funcionar en su contra si los datos no están en la memoria caché. La caché de Windows se implementa internamente mediante asignaciones de archivos. El administrador de memoria en Windows no proporciona un mecanismo de error de página asincrónico para administrar las asignaciones de archivos usadas por el administrador de caché. El administrador de caché puede comprobar si la página solicitada está en memoria, por lo que si emite una lectura asincrónica almacenada en caché y las páginas no están en memoria, el controlador del sistema de archivos supone que no quiere que el subproceso se bloquee y que la solicitud la controlará un grupo limitado de subprocesos de trabajo. El control se devuelve al programa después de la ReadFile llamada con la lectura pendiente.

Esto funciona bien para un pequeño número de solicitudes, pero dado que el grupo de subprocesos de trabajo es limitado (actualmente tres en un sistema de 16 MB), solo habrá unas pocas solicitudes en cola en el controlador de disco en un momento determinado. Si emite numerosas operaciones de E/S para datos que no están en la memoria caché, el administrador de caché y el administrador de memoria se saturan y las solicitudes se realizan sincrónicas.

El comportamiento del administrador de caché también se puede influir en función de si tiene acceso a un archivo de forma secuencial o aleatoria. Las ventajas de la memoria caché se ven más al acceder a los archivos secuencialmente. La FILE_FLAG_SEQUENTIAL_SCAN marca de la CreateFile llamada optimizará la memoria caché para este tipo de acceso. Sin embargo, si tiene acceso a los archivos de forma aleatoria, use la FILE_FLAG_RANDOM_ACCESS marca en CreateFile para indicar al administrador de caché que optimice su comportamiento para el acceso aleatorio.

No usar la memoria caché

La FILE_FLAG_NO_BUFFERING marca tiene el mayor efecto en el comportamiento del sistema de archivos para la operación asincrónica. Es la mejor manera de garantizar que las solicitudes de E/S sean asincrónicas. Indica al sistema de archivos que no use ningún mecanismo de caché.

Nota:

Hay algunas restricciones para usar esta marca que tienen que ver con la alineación del búfer de datos y el tamaño del sector del dispositivo. Para obtener más información, consulte la referencia de función en la documentación de la función CreateFile sobre el uso correcto de esta marca.

Resultados de pruebas del mundo real

Los siguientes son algunos resultados de prueba del código de ejemplo. La magnitud de los números no es importante aquí y varía de un equipo a otro, pero la relación de los números en comparación entre sí ilumina el efecto general de las marcas en el rendimiento.

Puede esperar ver resultados similares a uno de los siguientes:

  • Prueba 1

    Asynchronous, unbuffered I/O:  asynchio /f*.dat /n
    Operations completed out of the order in which they were requested.
       500 requests queued in 0.224264 second.
       500 requests completed in 4.982481 seconds.
    

    Esta prueba demuestra que el programa mencionado anteriormente emitió 500 solicitudes de E/S rápidamente y tenía mucho tiempo para realizar otro trabajo o emitir más solicitudes.

  • Prueba 2

    Synchronous, unbuffered I/O: asynchio /f*.dat /s /n
        Operations completed in the order issued.
        500 requests queued and completed in 4.495806 seconds.
    

    Esta prueba muestra que este programa pasó 4.495880 segundos llamando a ReadFile para completar sus operaciones, pero la prueba 1 pasó solo 0,224264 segundos para emitir las mismas solicitudes. En la prueba 2, no había tiempo adicional para que el programa realizara ningún trabajo en segundo plano.

  • Prueba 3

    Asynchronous, buffered I/O: asynchio /f*.dat
        Operations completed in the order issued.
        500 requests issued and completed in 0.251670 second.
    

    Esta prueba muestra la naturaleza sincrónica de la memoria caché. Todas las lecturas se emitieron y completaron en 0,251670 segundos. En otras palabras, las solicitudes asincrónicas se completaron sincrónicamente. Esta prueba también muestra el alto rendimiento del administrador de caché cuando los datos están en la memoria caché.

  • Prueba 4

    Synchronous, buffered I/O: asynchio /f*.dat /s
        Operations completed in the order issued.
        500 requests and completed in 0.217011 seconds.
    

    Esta prueba muestra los mismos resultados que en la prueba 3. Las lecturas sincrónicas de la memoria caché se completan un poco más rápido que las lecturas asincrónicas de la memoria caché. Esta prueba también muestra el alto rendimiento del administrador de caché cuando los datos están en la memoria caché.

Conclusión

Puede decidir qué método es mejor porque todo depende del tipo, tamaño y número de operaciones que realiza el programa.

El acceso a archivos predeterminado sin especificar ninguna marca especial en CreateFile es una operación sincrónica y almacenada en caché.

Nota:

Obtiene algún comportamiento asincrónico automático en este modo porque el controlador del sistema de archivos realiza lectura previa asincrónica predictiva y escritura diferida asincrónica de datos modificados. Aunque este comportamiento no convierte la E/S de la aplicación en asincrónica, es el caso ideal para la gran mayoría de las aplicaciones sencillas.

Por otro lado, si la aplicación no es sencilla, es posible que tenga que realizar alguna generación de perfiles y supervisión del rendimiento para determinar el mejor método, de forma similar a las pruebas que se ilustraron anteriormente en este artículo. Es útil generar perfiles del tiempo dedicado a la ReadFile función o WriteFile y, a continuación, comparar este tiempo con el tiempo que tardan las operaciones de E/S reales en completarse. Si la mayoría del tiempo se dedica a emitir realmente la E/S, la E/S se completa sincrónicamente. Sin embargo, si el tiempo dedicado a emitir solicitudes de E/S es relativamente pequeño en comparación con el tiempo que tardan las operaciones de E/S en completarse, las operaciones se tratan de forma asincrónica. El código de ejemplo mencionado anteriormente en este artículo usa la QueryPerformanceCounter función para realizar su propia generación de perfiles interna.

La supervisión del rendimiento puede ayudar a determinar la eficacia con la que el programa usa el disco y la memoria caché. El seguimiento de cualquiera de los contadores de rendimiento del objeto Cache indicará el rendimiento del administrador de caché. El seguimiento de los contadores de rendimiento de los objetos Disco físico o Disco lógico indicará el rendimiento de los sistemas de disco.

Hay varias utilidades que son útiles en la supervisión del rendimiento. PerfMon y DiskPerf son especialmente útiles. Para que el sistema recopile datos sobre el rendimiento de los sistemas de disco, primero debe emitir el DiskPerf comando. Después de emitir el comando, debe reiniciar el sistema para iniciar la recopilación de datos.

Referencias

E/S sincrónica y asincrónica