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 gebruik gemaakt van ReadFile 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 wanneer CreateFile 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 totdat de lopende 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

&NumberOfBytesRead ReadFile 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 die is doorgegeven aan ReadFile is betekenisloos.

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

Een ander voorbehoud bij asynchrone bewerkingen is dat u geen OVERLAPPED-structuur moet gebruiken totdat de lopende bewerking 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 nog steeds synchroon voltooid in de volgorde waarin ze zijn uitgevoerd, en retourneert geen van de ReadFile bewerkingen ONWAAR waarbij GetLastError()ERROR_IO_PENDING retourneert, wat betekent dat u geen tijd hebt voor taken op de achtergrond. 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 (dat beschikbaar is in Windows XP en latere versies), kunnen toepassingen bestanden efficiënt vergroten zonder dat er een prestatieverlies optreedt voor het invullen ervan.

Omdat het NTFS-bestandssysteem de gegevens niet tot de geldige gegevenslengte (VDL) die is gedefinieerd door SetFileValidData met nullen opvult, heeft deze functie beveiligingsimplicaties 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 case-code waarbij, als een I/O-aanvraag onmiddellijk kan worden voltooid, de bewerking wordt voltooid en de ReadFile of WriteFile functie 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 zijn gecached.

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 door gebruik te maken van bestandstoewijzingen. Het geheugenbeheer in Windows biedt geen asynchroon paginastoringmechanisme voor het beheren van de bestandstoewijzingen die worden gebruikt door de cachebeheerder. 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. De besturing wordt teruggegeven aan uw programma na uw ReadFile-aanroep, terwijl de leesbewerking nog in behandeling is.

Dit werkt prima voor een klein aantal aanvragen, maar omdat de pool van werkdraad beperkt is (momenteel drie op een 16-MB systeem), staan er op een bepaald moment nog maar enkele aanvragen in de wachtrij voor het schijfstuurprogramma. 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 seconden. 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 dat er speciale vlaggen worden opgegeven aan CreateFile, is een synchrone en gebufferde bewerking.

Notitie

U krijgt enig automatisch asynchroon gedrag in deze modus omdat het stuurprogramma van het bestandssysteem voorspellend asynchroon vooruit lezen en asynchrone luie 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 doorgebracht aan het werkelijk uitvoeren 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