Delen via


Asynchrone schijf-I/O wordt als synchroon weergegeven in Windows

Dit artikel helpt u bij het oplossen van het probleem waarbij het standaardgedrag voor I/O synchroon is, maar het wordt weergegeven als asynchroon.

Oorspronkelijke productversie: Windows
Oorspronkelijk KB-nummer: 156932

Samenvatting

Bestands-I/O in Microsoft Windows kan synchroon of asynchroon zijn. Het standaardgedrag voor I/O is synchroon, waarbij een I/O-functie wordt aangeroepen en geretourneerd wanneer de I/O is voltooid. Met asynchrone I/O kan een I/O-functie de uitvoering onmiddellijk terugbrengen naar de aanroeper, maar wordt ervan uitgegaan dat de I/O pas na enige tijd is voltooid. Het besturingssysteem meldt de beller wanneer de I/O is voltooid. In plaats daarvan kan de beller de status van de openstaande I/O-bewerking bepalen met behulp van services van het besturingssysteem.

Het voordeel van asynchrone I/O is dat de beller tijd heeft om ander werk uit te voeren of meer aanvragen uit te geven terwijl de I/O-bewerking wordt voltooid. De term Overlapped I/O wordt vaak gebruikt voor Asynchrone I/O en niet-overlappende I/O voor synchrone I/O. In dit artikel worden de termen Asynchroon en Synchroon voor I/O-bewerkingen gebruikt. In dit artikel wordt ervan uitgegaan dat de lezer bekend is met de functies Bestands-I/O, zoals CreateFile, ReadFile. WriteFile

Vaak gedragen asynchrone I/O-bewerkingen zich net als synchrone I/O-bewerkingen. Bepaalde voorwaarden die in dit artikel worden besproken in de latere secties, waardoor de I/O-bewerkingen synchroon worden voltooid. De aanroeper heeft geen tijd voor achtergrondwerk omdat de I/O-functies pas worden geretourneerd als de I/O is voltooid.

Verschillende functies zijn gerelateerd aan synchrone en asynchrone I/O. In dit artikel wordt gebruikgemaakt ReadFile van en WriteFile als voorbeelden. Goede alternatieven zouden zijn ReadFileEx en WriteFileEx. Hoewel in dit artikel specifiek alleen schijf-I/O wordt besproken, kunnen veel van de principes worden toegepast op andere typen I/O, zoals seriële I/O of netwerk-I/O.

Asynchrone I/O instellen

De FILE_FLAG_OVERLAPPED vlag moet worden opgegeven CreateFile wanneer het bestand wordt geopend. Met deze vlag kunnen I/O-bewerkingen op het bestand asynchroon worden uitgevoerd. Dit is een voorbeeld:

HANDLE hFile;

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

if (hFile == INVALID_HANDLE_VALUE)
      ErrorOpeningFile();

Wees voorzichtig wanneer u code voor asynchrone I/O gebruikt, omdat het systeem het recht behoudt om een bewerking synchroon te maken als dat nodig is. Het is dus het beste als u het programma schrijft om een I/O-bewerking correct af te handelen die synchroon of asynchroon kan worden voltooid. De voorbeeldcode demonstreert deze overweging.

Er zijn veel dingen die een programma kan doen terwijl wordt gewacht tot asynchrone bewerkingen zijn voltooid, zoals het in de wachtrij plaatsen van extra bewerkingen of het uitvoeren van achtergrondwerk. De volgende code verwerkt bijvoorbeeld overlappende en niet-overlappende voltooiing van een leesbewerking correct. Het doet niets meer dan wachten tot de openstaande I/O is voltooid:

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);

Notitie

&NumberOfBytesReadReadFile doorgegeven is anders dan &NumberOfBytesTransferred doorgegeven aan GetOverlappedResult. Als een bewerking asynchroon is gemaakt, wordt deze GetOverlappedResult gebruikt om het werkelijke aantal overgedragen bytes in de bewerking te bepalen nadat deze is voltooid. De &NumberOfBytesRead doorgegeven ReadFile is betekenisloos.

Als een bewerking daarentegen onmiddellijk is voltooid, &NumberOfBytesRead is doorgegeven ReadFile geldig voor het aantal bytes dat is gelezen. In dit geval negeert u de OVERLAPPED structuur die is ReadFiledoorgegeven aan ; gebruik deze niet met GetOverlappedResult of WaitForSingleObject.

Een andere kanttekening met asynchrone bewerking is dat u geen OVERLAPPED structuur moet gebruiken totdat de bewerking in behandeling is voltooid. Met andere woorden, als u drie uitstekende I/O-bewerkingen hebt, moet u drie OVERLAPPED structuren gebruiken. Als u een OVERLAPPED structuur opnieuw gebruikt, ontvangt u onvoorspelbare resultaten in de I/O-bewerkingen en kan er sprake zijn van beschadiging van gegevens. Daarnaast moet u deze correct initialiseren, zodat er geen restgegevens van invloed zijn op de nieuwe bewerking voordat u een OVERLAPPED structuur voor het eerst kunt gebruiken of voordat u deze opnieuw gebruikt nadat een eerdere bewerking is voltooid.

Hetzelfde type beperking is van toepassing op de gegevensbuffer die in een bewerking wordt gebruikt. Een gegevensbuffer mag pas worden gelezen of geschreven als de bijbehorende I/O-bewerking is voltooid; het lezen of schrijven van de buffer kan fouten en beschadigde gegevens veroorzaken.

Asynchrone I/O lijkt nog steeds synchroon te zijn

Als u de instructies eerder in dit artikel hebt gevolgd, zijn al uw I/O-bewerkingen echter nog steeds synchroon voltooid in de volgorde die is uitgegeven en geen van de ReadFile bewerkingen retourneert ONWAAR met GetLastError() retourneren ERROR_IO_PENDING, wat betekent dat u geen tijd hebt voor achtergrondwerk. Waarom gebeurt dit?

Er zijn een aantal redenen waarom I/O-bewerkingen synchroon worden voltooid, zelfs als u hebt gecodeerd voor asynchrone bewerkingen.

Compressie

Een obstakel voor asynchrone bewerking is NTFS-compressie (New Technology File System). Het bestandssysteemstuurprogramma heeft asynchroon geen toegang tot gecomprimeerde bestanden; in plaats daarvan worden alle bewerkingen synchroon gemaakt. Deze obstructie is niet van toepassing op bestanden die zijn gecomprimeerd met hulpprogramma's die vergelijkbaar zijn met COMPRESS of PKZIP.

NTFS-versleuteling

Net als bij compressie zorgt bestandsversleuteling ervoor dat het systeemstuurprogramma asynchrone I/O converteert naar synchroon. Als de bestanden worden ontsleuteld, zijn de I/O-aanvragen asynchroon.

Een bestand uitbreiden

Een andere reden dat I/O-bewerkingen synchroon worden voltooid, zijn de bewerkingen zelf. In Windows is elke schrijfbewerking naar een bestand dat de lengte verlengt synchroon.

Notitie

Toepassingen kunnen de eerder genoemde schrijfbewerking asynchroon maken door de geldige gegevenslengte van het bestand te wijzigen met behulp van de SetFileValidData functie en vervolgens een WriteFile.

Met SetFileValidData (wat beschikbaar is in Windows XP en latere versies), kunnen toepassingen bestanden efficiënt uitbreiden zonder dat er een prestatiestraf wordt gemaakt voor het invullen ervan.

Omdat het NTFS-bestandssysteem de gegevens niet op nul vult tot de geldige gegevenslengte (NTFS) die is gedefinieerd SetFileValidData, heeft deze functie gevolgen voor de beveiliging waarbij aan het bestand clusters kunnen worden toegewezen die eerder door andere bestanden zijn bezet. SetFileValidData Daarom is vereist dat de beller de nieuwe SeManageVolumePrivilege functie heeft ingeschakeld (standaard is dit alleen toegewezen aan beheerders). Microsoft raadt onafhankelijke softwareleveranciers (ISV's) aan zorgvuldig na te denken over de gevolgen van het gebruik van een dergelijke functie.

Cache

De meeste I/O-stuurprogramma's (schijf, communicatie en andere) hebben speciale casecode waarbij, als een I/O-aanvraag onmiddellijk kan worden voltooid, de bewerking wordt voltooid en de ReadFile functie WriteFile WAAR retourneert. Op alle manieren lijken deze typen bewerkingen synchroon te zijn. Voor een schijfapparaat kan doorgaans een I/O-aanvraag onmiddellijk worden voltooid wanneer de gegevens in het geheugen in de cache worden opgeslagen.

Gegevens bevinden zich niet in de cache

Het cacheschema kan echter wel tegen u werken als de gegevens zich niet in de cache bevinden. De Windows-cache wordt intern geïmplementeerd met behulp van bestandstoewijzingen. Het geheugenbeheer in Windows biedt geen asynchroon paginafoutmechanisme voor het beheren van de bestandstoewijzingen die worden gebruikt door de cachebeheer. De cachebeheerder kan controleren of de aangevraagde pagina zich in het geheugen bevindt, dus als u een asynchrone leesbewerking opgeeft en de pagina's zich niet in het geheugen bevinden, gaat het bestandssysteemstuurprogramma ervan uit dat u de thread niet wilt blokkeren en dat de aanvraag wordt verwerkt door een beperkte groep werkrolthreads. Het besturingselement wordt teruggezet naar uw programma na het ReadFile gesprek met de leesbewerking die nog in behandeling is.

Dit werkt prima voor een klein aantal aanvragen, maar omdat de groep werkthreads beperkt is (momenteel drie op een systeem van 16 MB), worden er nog maar enkele aanvragen in de wachtrij geplaatst voor het schijfstuurprogramma op een bepaald moment. Als u talloze I/O-bewerkingen uitvoert voor gegevens die zich niet in de cache bevinden, raken de cachebeheer en geheugenbeheerder verzadigd en worden uw aanvragen synchroon uitgevoerd.

Het gedrag van de cachebeheer kan ook worden beïnvloed op basis van of u een bestand sequentieel of willekeurig opent. Voordelen van de cache worden het meest gezien wanneer u bestanden opeenvolgend opent. De FILE_FLAG_SEQUENTIAL_SCAN vlag in de CreateFile aanroep optimaliseert de cache voor dit type toegang. Als u echter bestanden op willekeurige wijze opent, gebruikt u de FILE_FLAG_RANDOM_ACCESS vlag om CreateFile de cachebeheerder te instrueren om het gedrag ervan te optimaliseren voor willekeurige toegang.

Gebruik de cache niet

De FILE_FLAG_NO_BUFFERING vlag heeft het meeste effect op het gedrag van het bestandssysteem voor asynchrone bewerkingen. Het is de beste manier om te garanderen dat I/O-aanvragen asynchroon zijn. Het geeft het bestandssysteem de opdracht om helemaal geen cachemechanisme te gebruiken.

Notitie

Er zijn enkele beperkingen voor het gebruik van deze vlag die te maken heeft met de uitlijning van de gegevensbuffer en de sectorgrootte van het apparaat. Zie de functiereferentie in de documentatie voor de functie CreateFile over het gebruik van deze vlag voor meer informatie.

Resultaten van de echte wereldtest

Hier volgen enkele testresultaten uit de voorbeeldcode. De grootte van de getallen is hier niet belangrijk en varieert van computer tot computer, maar de relatie van de getallen vergeleken met elkaar verlicht het algemene effect van de vlaggen op prestaties.

U kunt verwachten dat er resultaten worden weergegeven die vergelijkbaar zijn met een van de volgende:

  • Test 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.
    

    Deze test laat zien dat het eerder genoemde programma snel 500 I/O-aanvragen heeft uitgegeven en veel tijd heeft gehad om andere werkzaamheden uit te voeren of meer aanvragen uit te geven.

  • Test 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.
    

    Deze test laat zien dat dit programma 4.495880 seconden heeft besteed aan het aanroepen van ReadFile om de bewerkingen te voltooien, maar de test 1 heeft slechts 0,224264 seconde besteed om dezelfde aanvragen uit te geven. In test 2 was er geen extra tijd voor het programma om achtergrondwerk uit te voeren.

  • Test 3

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

    Deze test demonstreert de synchrone aard van de cache. Alle leesbewerkingen zijn uitgegeven en voltooid in 0,251670 seconde. Met andere woorden, asynchrone aanvragen zijn synchroon voltooid. Deze test demonstreert ook de hoge prestaties van de cachebeheer wanneer gegevens zich in de cache bevinden.

  • Test 4

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

    Deze test demonstreert dezelfde resultaten als in test 3. Synchrone leesbewerkingen uit de cache voltooien iets sneller dan asynchrone leesbewerkingen uit de cache. Deze test demonstreert ook de hoge prestaties van de cachebeheer wanneer gegevens zich in de cache bevinden.

Conclusie

U kunt bepalen welke methode het beste is omdat deze allemaal afhankelijk is van het type, de grootte en het aantal bewerkingen dat uw programma uitvoert.

De standaardbestandstoegang zonder speciale vlaggen op te CreateFile geven, is een synchrone en in de cache opgeslagen bewerking.

Notitie

U krijgt automatisch asynchroon gedrag in deze modus omdat het stuurprogramma van het bestandssysteem voorspellende asynchrone lees-voor- en asynchrone lasynchrone schrijfbewerkingen van gewijzigde gegevens uitvoert. Hoewel dit gedrag de I/O van de toepassing niet asynchroon maakt, is dit het ideale geval voor de overgrote meerderheid van eenvoudige toepassingen.

Als uw toepassing echter niet eenvoudig is, moet u mogelijk enkele profilerings- en prestatiebewaking uitvoeren om de beste methode te bepalen, vergelijkbaar met de tests die eerder in dit artikel zijn geïllustreerd. Het profileren van de tijd die is besteed aan de ReadFile of WriteFile functie en deze tijd vervolgens vergelijken met hoe lang het duurt voordat werkelijke I/O-bewerkingen zijn voltooid, is nuttig. Als het merendeel van de tijd wordt besteed aan het daadwerkelijk uitgeven van de I/O, wordt uw I/O synchroon voltooid. Als de tijd die is besteed aan het uitgeven van I/O-aanvragen echter relatief klein is in vergelijking met de tijd die nodig is om de I/O-bewerkingen te voltooien, worden uw bewerkingen asynchroon behandeld. De voorbeeldcode die eerder in dit artikel wordt genoemd, gebruikt de QueryPerformanceCounter functie om een eigen interne profilering uit te voeren.

Prestatiebewaking kan helpen bepalen hoe efficiënt uw programma gebruikmaakt van de schijf en de cache. Als u een van de prestatiemeteritems voor het Cache-object bijhoudt, worden de prestaties van het cachebeheer aangegeven. Het bijhouden van de prestatiemeteritems voor de objecten Fysieke schijf of logische schijf geeft de prestaties van de schijfsystemen aan.

Er zijn verschillende hulpprogramma's die nuttig zijn bij het bewaken van prestaties. PerfMon en DiskPerf zijn vooral nuttig. Als u wilt dat het systeem gegevens over de prestaties van de schijfsystemen verzamelt, moet u eerst de DiskPerf opdracht geven. Nadat u de opdracht hebt uitgevoerd, moet u het systeem opnieuw opstarten om de gegevensverzameling te starten.

Verwijzingen

Synchrone en asynchrone I/O