Compartir a través de


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 archivos 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 devuelve cuando se completa la E/S. La E/S asincrónica permite que una función de E/S devuelva la ejecución inmediatamente al autor de la llamada, pero no se supone que la E/S se complete 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 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 igual que la E/S sincrónica. Ciertas condiciones que describe 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 complete la E/S.

Varias funciones están relacionadas con la E/S sincrónica y asincrónica. En este artículo se usa ReadFile y WriteFile como ejemplos. Buenas alternativas serían ReadFileEx y WriteFileEx. Aunque en este artículo solo se describe la E/S de disco específicamente, muchos de los principios se pueden aplicar a otros tipos de E/S, como la E/S serie o la 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 realizar operaciones de E/S en el archivo de forma asincrónica. Este es 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 programar 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 trabajos 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 a ReadFile es diferente de &NumberOfBytesTransferred pasar 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 se completa inmediatamente una operación, pasar &NumberOfBytesRead 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 haya completado 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 imprevisibles en las operaciones de E/S y puede experimentar daños en los datos. Además, debe inicializarla correctamente, por lo que ningún dato dejado afecta a la nueva operación antes de poder usar una OVERLAPPED estructura por primera vez o antes de volver a usarla 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 provocar errores y datos dañados.

La E/S asincrónica sigue siendo sincrónica

Si ha seguido las instrucciones anteriores en este artículo, sin embargo, todas las operaciones de E/S siguen completando normalmente de forma sincrónica 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 para la operación asincrónica es la compresión del Sistema de archivos de nueva tecnología (NTFS). El controlador del sistema de archivos no accederá a 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 un WriteFile.

Con SetFileValidData (que está disponible en Windows XP y versiones posteriores), las aplicaciones pueden extender archivos de forma eficaz sin incurrir en una penalización de rendimiento para rellenarlos sin límites.

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

instancias y claves

La mayoría de los controladores de E/S (disco, comunicaciones y otros) tienen código de caso especial donde, si se puede completar inmediatamente una solicitud de E/S, la operación se completará y la ReadFile función o WriteFile devolverá TRUE. De todas formas, 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 caché

Sin embargo, el esquema de caché puede funcionar con usted 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 en caché asincrónica y las páginas no están en memoria, el controlador del sistema de archivos supone que no desea que el subproceso esté bloqueado y que la solicitud se controlará mediante un grupo limitado de subprocesos de trabajo. El control se devuelve al programa después ReadFile de la 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 está limitado (actualmente tres en un sistema de 16 MB), todavía habrá solo algunas solicitudes en cola al controlador de disco en un momento determinado. Si emite numerosas operaciones de E/S para los 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ónicamente.

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 la mayoría al acceder a archivos secuencialmente. La FILE_FLAG_SEQUENTIAL_SCAN marca de la CreateFile llamada optimizará la memoria caché para este tipo de acceso. Sin embargo, si accede a 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 use 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 son asincrónicas. Indica al sistema de archivos que no use ningún mecanismo de caché.

Nota:

Hay algunas restricciones para usar esta marca que tiene 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 cómo usar esta marca correctamente.

Resultados de pruebas reales

A continuación se muestran algunos resultados de prueba del código de ejemplo. La magnitud de los números no es importante aquí y varía de equipo a equipo, 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ó rápidamente 500 solicitudes de E/S y tuvo 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 demuestra 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. Es decir, 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, el tamaño y el número de operaciones que realiza el programa.

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

Nota:

En este modo se obtiene un comportamiento asincrónico automático, ya que el controlador del sistema de archivos realiza la lectura anticipada asincrónica predictiva y la escritura diferida asincrónica de datos modificados. Aunque este comportamiento no hace que la E/S de la aplicación sea asincrónica, es el caso ideal para la gran mayoría de las aplicaciones simples.

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, similar a las pruebas que se muestran anteriormente en este artículo. Generar perfiles del tiempo invertido en la ReadFile función o WriteFile y, a continuación, comparar este tiempo con el tiempo que tardan en completarse las operaciones de E/S reales es útil. Si la mayoría del tiempo se dedica realmente a emitir la E/S, la E/S se está completando sincrónicamente. Sin embargo, si el tiempo dedicado a emitir solicitudes de E/S es relativamente pequeño en comparación con el tiempo necesario para que se completen las operaciones de E/S, 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 del programa mediante 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